diff options
Diffstat (limited to 'contrib/file/src')
38 files changed, 16784 insertions, 0 deletions
diff --git a/contrib/file/src/Makefile.am b/contrib/file/src/Makefile.am new file mode 100644 index 0000000..e3196ce --- /dev/null +++ b/contrib/file/src/Makefile.am @@ -0,0 +1,30 @@ +MAGIC = $(pkgdatadir)/magic +lib_LTLIBRARIES = libmagic.la +include_HEADERS = magic.h + +bin_PROGRAMS = file + +AM_CPPFLAGS = -DMAGIC='"$(MAGIC)"' +AM_CFLAGS = $(CFLAG_VISIBILITY) @WARNINGS@ + +libmagic_la_SOURCES = magic.c apprentice.c softmagic.c ascmagic.c \ + encoding.c compress.c is_tar.c readelf.c print.c fsmagic.c \ + funcs.c file.h readelf.h tar.h apptype.c \ + file_opts.h elfclass.h mygetopt.h cdf.c cdf_time.c readcdf.c cdf.h +libmagic_la_LDFLAGS = -no-undefined -version-info 1:0:0 +if MINGW +MINGWLIBS = -lgnurx -lshlwapi +else +MINGWLIBS = +endif +libmagic_la_LIBADD = $(LTLIBOBJS) $(MINGWLIBS) + +file_SOURCES = file.c +file_LDADD = libmagic.la +CLEANFILES = magic.h +EXTRA_DIST = magic.h.in +HDR= $(top_srcdir)/src/magic.h.in +BUILT_SOURCES = magic.h + +magic.h: ${HDR} + sed -e "s/X.YY/$$(echo @VERSION@ | tr -d .)/" < ${HDR} > $@ diff --git a/contrib/file/src/Makefile.in b/contrib/file/src/Makefile.in new file mode 100644 index 0000000..970ff0b --- /dev/null +++ b/contrib/file/src/Makefile.in @@ -0,0 +1,783 @@ +# Makefile.in generated by automake 1.14 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = file$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am pread.c \ + ctime_r.c getline.c vasprintf.c asprintf.c asctime_r.c \ + fmtcheck.c strlcpy.c getopt_long.c strcasestr.c strlcat.c \ + $(top_srcdir)/depcomp $(include_HEADERS) +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libmagic_la_DEPENDENCIES = $(LTLIBOBJS) $(am__DEPENDENCIES_1) +am_libmagic_la_OBJECTS = magic.lo apprentice.lo softmagic.lo \ + ascmagic.lo encoding.lo compress.lo is_tar.lo readelf.lo \ + print.lo fsmagic.lo funcs.lo apptype.lo cdf.lo cdf_time.lo \ + readcdf.lo +libmagic_la_OBJECTS = $(am_libmagic_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libmagic_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libmagic_la_LDFLAGS) $(LDFLAGS) -o $@ +PROGRAMS = $(bin_PROGRAMS) +am_file_OBJECTS = file.$(OBJEXT) +file_OBJECTS = $(am_file_OBJECTS) +file_DEPENDENCIES = libmagic.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libmagic_la_SOURCES) $(file_SOURCES) +DIST_SOURCES = $(libmagic_la_SOURCES) $(file_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +pkgdatadir = @pkgdatadir@ +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MINGW = @MINGW@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WARNINGS = @WARNINGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +fsect = @fsect@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +MAGIC = $(pkgdatadir)/magic +lib_LTLIBRARIES = libmagic.la +include_HEADERS = magic.h +AM_CPPFLAGS = -DMAGIC='"$(MAGIC)"' +AM_CFLAGS = $(CFLAG_VISIBILITY) @WARNINGS@ +libmagic_la_SOURCES = magic.c apprentice.c softmagic.c ascmagic.c \ + encoding.c compress.c is_tar.c readelf.c print.c fsmagic.c \ + funcs.c file.h readelf.h tar.h apptype.c \ + file_opts.h elfclass.h mygetopt.h cdf.c cdf_time.c readcdf.c cdf.h + +libmagic_la_LDFLAGS = -no-undefined -version-info 1:0:0 +@MINGW_FALSE@MINGWLIBS = +@MINGW_TRUE@MINGWLIBS = -lgnurx -lshlwapi +libmagic_la_LIBADD = $(LTLIBOBJS) $(MINGWLIBS) +file_SOURCES = file.c +file_LDADD = libmagic.la +CLEANFILES = magic.h +EXTRA_DIST = magic.h.in +HDR = $(top_srcdir)/src/magic.h.in +BUILT_SOURCES = magic.h +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libmagic.la: $(libmagic_la_OBJECTS) $(libmagic_la_DEPENDENCIES) $(EXTRA_libmagic_la_DEPENDENCIES) + $(AM_V_CCLD)$(libmagic_la_LINK) -rpath $(libdir) $(libmagic_la_OBJECTS) $(libmagic_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +file$(EXEEXT): $(file_OBJECTS) $(file_DEPENDENCIES) $(EXTRA_file_DEPENDENCIES) + @rm -f file$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(file_OBJECTS) $(file_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asctime_r.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asprintf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ctime_r.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/fmtcheck.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getline.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getopt_long.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/pread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasestr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strlcat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strlcpy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vasprintf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apprentice.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apptype.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ascmagic.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdf_time.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoding.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsmagic.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/funcs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/is_tar.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/magic.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readcdf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readelf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/softmagic.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ + done + +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf $(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf $(DEPDIR) ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ + uninstall-libLTLIBRARIES + +.MAKE: all check install install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am \ + install-includeHEADERS install-info install-info-am \ + install-libLTLIBRARIES install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-includeHEADERS uninstall-libLTLIBRARIES + + +magic.h: ${HDR} + sed -e "s/X.YY/$$(echo @VERSION@ | tr -d .)/" < ${HDR} > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/contrib/file/src/apprentice.c b/contrib/file/src/apprentice.c new file mode 100644 index 0000000..71e9af6 --- /dev/null +++ b/contrib/file/src/apprentice.c @@ -0,0 +1,3082 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * apprentice - make one pass through /etc/magic, learning its secrets. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: apprentice.c,v 1.211 2014/06/03 19:01:34 christos Exp $") +#endif /* lint */ + +#include "magic.h" +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDDEF_H +#include <stddef.h> +#endif +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <fcntl.h> +#ifdef QUICK +#include <sys/mman.h> +#endif +#include <dirent.h> +#if defined(HAVE_LIMITS_H) +#include <limits.h> +#endif + +#ifndef SSIZE_MAX +#define MAXMAGIC_SIZE ((ssize_t)0x7fffffff) +#else +#define MAXMAGIC_SIZE SSIZE_MAX +#endif + +#define EATAB {while (isascii((unsigned char) *l) && \ + isspace((unsigned char) *l)) ++l;} +#define LOWCASE(l) (isupper((unsigned char) (l)) ? \ + tolower((unsigned char) (l)) : (l)) +/* + * Work around a bug in headers on Digital Unix. + * At least confirmed for: OSF1 V4.0 878 + */ +#if defined(__osf__) && defined(__DECC) +#ifdef MAP_FAILED +#undef MAP_FAILED +#endif +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED (void *) -1 +#endif + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif + +#define ALLOC_CHUNK (size_t)10 +#define ALLOC_INCR (size_t)200 + +struct magic_entry { + struct magic *mp; + uint32_t cont_count; + uint32_t max_count; +}; + +struct magic_entry_set { + struct magic_entry *me; + uint32_t count; + uint32_t max; +}; + +struct magic_map { + void *p; + size_t len; + struct magic *magic[MAGIC_SETS]; + uint32_t nmagic[MAGIC_SETS]; +}; + +int file_formats[FILE_NAMES_SIZE]; +const size_t file_nformats = FILE_NAMES_SIZE; +const char *file_names[FILE_NAMES_SIZE]; +const size_t file_nnames = FILE_NAMES_SIZE; + +private int getvalue(struct magic_set *ms, struct magic *, const char **, int); +private int hextoint(int); +private const char *getstr(struct magic_set *, struct magic *, const char *, + int); +private int parse(struct magic_set *, struct magic_entry *, const char *, + size_t, int); +private void eatsize(const char **); +private int apprentice_1(struct magic_set *, const char *, int); +private size_t apprentice_magic_strength(const struct magic *); +private int apprentice_sort(const void *, const void *); +private void apprentice_list(struct mlist *, int ); +private struct magic_map *apprentice_load(struct magic_set *, + const char *, int); +private struct mlist *mlist_alloc(void); +private void mlist_free(struct mlist *); +private void byteswap(struct magic *, uint32_t); +private void bs1(struct magic *); +private uint16_t swap2(uint16_t); +private uint32_t swap4(uint32_t); +private uint64_t swap8(uint64_t); +private char *mkdbname(struct magic_set *, const char *, int); +private struct magic_map *apprentice_map(struct magic_set *, const char *); +private void apprentice_unmap(struct magic_map *); +private int apprentice_compile(struct magic_set *, struct magic_map *, + const char *); +private int check_format_type(const char *, int); +private int check_format(struct magic_set *, struct magic *); +private int get_op(char); +private int parse_mime(struct magic_set *, struct magic_entry *, const char *); +private int parse_strength(struct magic_set *, struct magic_entry *, const char *); +private int parse_apple(struct magic_set *, struct magic_entry *, const char *); + + +private size_t magicsize = sizeof(struct magic); + +private const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc"; + +private struct { + const char *name; + size_t len; + int (*fun)(struct magic_set *, struct magic_entry *, const char *); +} bang[] = { +#define DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name } + DECLARE_FIELD(mime), + DECLARE_FIELD(apple), + DECLARE_FIELD(strength), +#undef DECLARE_FIELD + { NULL, 0, NULL } +}; + +#ifdef COMPILE_ONLY + +int main(int, char *[]); + +int +main(int argc, char *argv[]) +{ + int ret; + struct magic_set *ms; + char *progname; + + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; + + if (argc != 2) { + (void)fprintf(stderr, "Usage: %s file\n", progname); + return 1; + } + + if ((ms = magic_open(MAGIC_CHECK)) == NULL) { + (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + return 1; + } + ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0; + if (ret == 1) + (void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms)); + magic_close(ms); + return ret; +} +#endif /* COMPILE_ONLY */ + +struct type_tbl_s { + const char name[16]; + const size_t len; + const int type; + const int format; +}; + +/* + * XXX - the actual Single UNIX Specification says that "long" means "long", + * as in the C data type, but we treat it as meaning "4-byte integer". + * Given that the OS X version of file 5.04 did the same, I guess that passes + * the actual test; having "long" be dependent on how big a "long" is on + * the machine running "file" is silly. + */ +static const struct type_tbl_s type_tbl[] = { +# define XX(s) s, (sizeof(s) - 1) +# define XX_NULL "", 0 + { XX("invalid"), FILE_INVALID, FILE_FMT_NONE }, + { XX("byte"), FILE_BYTE, FILE_FMT_NUM }, + { XX("short"), FILE_SHORT, FILE_FMT_NUM }, + { XX("default"), FILE_DEFAULT, FILE_FMT_NONE }, + { XX("long"), FILE_LONG, FILE_FMT_NUM }, + { XX("string"), FILE_STRING, FILE_FMT_STR }, + { XX("date"), FILE_DATE, FILE_FMT_STR }, + { XX("beshort"), FILE_BESHORT, FILE_FMT_NUM }, + { XX("belong"), FILE_BELONG, FILE_FMT_NUM }, + { XX("bedate"), FILE_BEDATE, FILE_FMT_STR }, + { XX("leshort"), FILE_LESHORT, FILE_FMT_NUM }, + { XX("lelong"), FILE_LELONG, FILE_FMT_NUM }, + { XX("ledate"), FILE_LEDATE, FILE_FMT_STR }, + { XX("pstring"), FILE_PSTRING, FILE_FMT_STR }, + { XX("ldate"), FILE_LDATE, FILE_FMT_STR }, + { XX("beldate"), FILE_BELDATE, FILE_FMT_STR }, + { XX("leldate"), FILE_LELDATE, FILE_FMT_STR }, + { XX("regex"), FILE_REGEX, FILE_FMT_STR }, + { XX("bestring16"), FILE_BESTRING16, FILE_FMT_STR }, + { XX("lestring16"), FILE_LESTRING16, FILE_FMT_STR }, + { XX("search"), FILE_SEARCH, FILE_FMT_STR }, + { XX("medate"), FILE_MEDATE, FILE_FMT_STR }, + { XX("meldate"), FILE_MELDATE, FILE_FMT_STR }, + { XX("melong"), FILE_MELONG, FILE_FMT_NUM }, + { XX("quad"), FILE_QUAD, FILE_FMT_QUAD }, + { XX("lequad"), FILE_LEQUAD, FILE_FMT_QUAD }, + { XX("bequad"), FILE_BEQUAD, FILE_FMT_QUAD }, + { XX("qdate"), FILE_QDATE, FILE_FMT_STR }, + { XX("leqdate"), FILE_LEQDATE, FILE_FMT_STR }, + { XX("beqdate"), FILE_BEQDATE, FILE_FMT_STR }, + { XX("qldate"), FILE_QLDATE, FILE_FMT_STR }, + { XX("leqldate"), FILE_LEQLDATE, FILE_FMT_STR }, + { XX("beqldate"), FILE_BEQLDATE, FILE_FMT_STR }, + { XX("float"), FILE_FLOAT, FILE_FMT_FLOAT }, + { XX("befloat"), FILE_BEFLOAT, FILE_FMT_FLOAT }, + { XX("lefloat"), FILE_LEFLOAT, FILE_FMT_FLOAT }, + { XX("double"), FILE_DOUBLE, FILE_FMT_DOUBLE }, + { XX("bedouble"), FILE_BEDOUBLE, FILE_FMT_DOUBLE }, + { XX("ledouble"), FILE_LEDOUBLE, FILE_FMT_DOUBLE }, + { XX("leid3"), FILE_LEID3, FILE_FMT_NUM }, + { XX("beid3"), FILE_BEID3, FILE_FMT_NUM }, + { XX("indirect"), FILE_INDIRECT, FILE_FMT_NUM }, + { XX("qwdate"), FILE_QWDATE, FILE_FMT_STR }, + { XX("leqwdate"), FILE_LEQWDATE, FILE_FMT_STR }, + { XX("beqwdate"), FILE_BEQWDATE, FILE_FMT_STR }, + { XX("name"), FILE_NAME, FILE_FMT_NONE }, + { XX("use"), FILE_USE, FILE_FMT_NONE }, + { XX("clear"), FILE_CLEAR, FILE_FMT_NONE }, + { XX_NULL, FILE_INVALID, FILE_FMT_NONE }, +}; + +/* + * These are not types, and cannot be preceded by "u" to make them + * unsigned. + */ +static const struct type_tbl_s special_tbl[] = { + { XX("name"), FILE_NAME, FILE_FMT_STR }, + { XX("use"), FILE_USE, FILE_FMT_STR }, + { XX_NULL, FILE_INVALID, FILE_FMT_NONE }, +}; +# undef XX +# undef XX_NULL + +private int +get_type(const struct type_tbl_s *tbl, const char *l, const char **t) +{ + const struct type_tbl_s *p; + + for (p = tbl; p->len; p++) { + if (strncmp(l, p->name, p->len) == 0) { + if (t) + *t = l + p->len; + break; + } + } + return p->type; +} + +private int +get_standard_integer_type(const char *l, const char **t) +{ + int type; + + if (isalpha((unsigned char)l[1])) { + switch (l[1]) { + case 'C': + /* "dC" and "uC" */ + type = FILE_BYTE; + break; + case 'S': + /* "dS" and "uS" */ + type = FILE_SHORT; + break; + case 'I': + case 'L': + /* + * "dI", "dL", "uI", and "uL". + * + * XXX - the actual Single UNIX Specification says + * that "L" means "long", as in the C data type, + * but we treat it as meaning "4-byte integer". + * Given that the OS X version of file 5.04 did + * the same, I guess that passes the actual SUS + * validation suite; having "dL" be dependent on + * how big a "long" is on the machine running + * "file" is silly. + */ + type = FILE_LONG; + break; + case 'Q': + /* "dQ" and "uQ" */ + type = FILE_QUAD; + break; + default: + /* "d{anything else}", "u{anything else}" */ + return FILE_INVALID; + } + l += 2; + } else if (isdigit((unsigned char)l[1])) { + /* + * "d{num}" and "u{num}"; we only support {num} values + * of 1, 2, 4, and 8 - the Single UNIX Specification + * doesn't say anything about whether arbitrary + * values should be supported, but both the Solaris 10 + * and OS X Mountain Lion versions of file passed the + * Single UNIX Specification validation suite, and + * neither of them support values bigger than 8 or + * non-power-of-2 values. + */ + if (isdigit((unsigned char)l[2])) { + /* Multi-digit, so > 9 */ + return FILE_INVALID; + } + switch (l[1]) { + case '1': + type = FILE_BYTE; + break; + case '2': + type = FILE_SHORT; + break; + case '4': + type = FILE_LONG; + break; + case '8': + type = FILE_QUAD; + break; + default: + /* XXX - what about 3, 5, 6, or 7? */ + return FILE_INVALID; + } + l += 2; + } else { + /* + * "d" or "u" by itself. + */ + type = FILE_LONG; + ++l; + } + if (t) + *t = l; + return type; +} + +private void +init_file_tables(void) +{ + static int done = 0; + const struct type_tbl_s *p; + + if (done) + return; + done++; + + for (p = type_tbl; p->len; p++) { + assert(p->type < FILE_NAMES_SIZE); + file_names[p->type] = p->name; + file_formats[p->type] = p->format; + } + assert(p - type_tbl == FILE_NAMES_SIZE); +} + +private int +add_mlist(struct mlist *mlp, struct magic_map *map, size_t idx) +{ + struct mlist *ml; + + if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL) + return -1; + + ml->map = idx == 0 ? map : NULL; + ml->magic = map->magic[idx]; + ml->nmagic = map->nmagic[idx]; + + mlp->prev->next = ml; + ml->prev = mlp->prev; + ml->next = mlp; + mlp->prev = ml; + return 0; +} + +/* + * Handle one file or directory. + */ +private int +apprentice_1(struct magic_set *ms, const char *fn, int action) +{ +#ifndef COMPILE_ONLY + struct mlist *ml; +#endif /* COMPILE_ONLY */ + struct magic_map *map; +#ifndef COMPILE_ONLY + size_t i; +#endif /* COMPILE_ONLY */ + + if (magicsize != FILE_MAGICSIZE) { + file_error(ms, 0, "magic element size %lu != %lu", + (unsigned long)sizeof(*map->magic[0]), + (unsigned long)FILE_MAGICSIZE); + return -1; + } + + if (action == FILE_COMPILE) { + map = apprentice_load(ms, fn, action); + if (map == NULL) + return -1; + return apprentice_compile(ms, map, fn); + } + +#ifndef COMPILE_ONLY + map = apprentice_map(ms, fn); + if (map == NULL) { + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "using regular magic file `%s'", fn); + map = apprentice_load(ms, fn, action); + if (map == NULL) + return -1; + } + + for (i = 0; i < MAGIC_SETS; i++) { + if (add_mlist(ms->mlist[i], map, i) == -1) { + file_oomem(ms, sizeof(*ml)); + apprentice_unmap(map); + return -1; + } + } + + if (action == FILE_LIST) { + for (i = 0; i < MAGIC_SETS; i++) { + printf("Set %zu:\nBinary patterns:\n", i); + apprentice_list(ms->mlist[i], BINTEST); + printf("Text patterns:\n"); + apprentice_list(ms->mlist[i], TEXTTEST); + } + } +#endif /* COMPILE_ONLY */ + + return 0; +} + +protected void +file_ms_free(struct magic_set *ms) +{ + size_t i; + if (ms == NULL) + return; + for (i = 0; i < MAGIC_SETS; i++) + mlist_free(ms->mlist[i]); + free(ms->o.pbuf); + free(ms->o.buf); + free(ms->c.li); + free(ms); +} + +protected struct magic_set * +file_ms_alloc(int flags) +{ + struct magic_set *ms; + size_t i, len; + + if ((ms = CAST(struct magic_set *, calloc((size_t)1, + sizeof(struct magic_set)))) == NULL) + return NULL; + + if (magic_setflags(ms, flags) == -1) { + errno = EINVAL; + goto free; + } + + ms->o.buf = ms->o.pbuf = NULL; + len = (ms->c.len = 10) * sizeof(*ms->c.li); + + if ((ms->c.li = CAST(struct level_info *, malloc(len))) == NULL) + goto free; + + ms->event_flags = 0; + ms->error = -1; + for (i = 0; i < MAGIC_SETS; i++) + ms->mlist[i] = NULL; + ms->file = "unknown"; + ms->line = 0; + return ms; +free: + free(ms); + return NULL; +} + +private void +apprentice_unmap(struct magic_map *map) +{ + if (map == NULL) + return; + if (map->p != NULL) { +#ifdef QUICK + if (map->len) + (void)munmap(map->p, map->len); + else +#endif + free(map->p); + } else { + uint32_t j; + for (j = 0; j < MAGIC_SETS; j++) + free(map->magic[j]); + } + free(map); +} + +private struct mlist * +mlist_alloc(void) +{ + struct mlist *mlist; + if ((mlist = CAST(struct mlist *, calloc(1, sizeof(*mlist)))) == NULL) { + return NULL; + } + mlist->next = mlist->prev = mlist; + return mlist; +} + +private void +mlist_free(struct mlist *mlist) +{ + struct mlist *ml; + + if (mlist == NULL) + return; + + for (ml = mlist->next; ml != mlist;) { + struct mlist *next = ml->next; + if (ml->map) + apprentice_unmap(ml->map); + free(ml); + ml = next; + } + free(ml); +} + +/* const char *fn: list of magic files and directories */ +protected int +file_apprentice(struct magic_set *ms, const char *fn, int action) +{ + char *p, *mfn; + int file_err, errs = -1; + size_t i; + + if (ms->mlist[0] != NULL) + file_reset(ms); + + if ((fn = magic_getpath(fn, action)) == NULL) + return -1; + + init_file_tables(); + + if ((mfn = strdup(fn)) == NULL) { + file_oomem(ms, strlen(fn)); + return -1; + } + + for (i = 0; i < MAGIC_SETS; i++) { + mlist_free(ms->mlist[i]); + if ((ms->mlist[i] = mlist_alloc()) == NULL) { + file_oomem(ms, sizeof(*ms->mlist[i])); + if (i != 0) { + --i; + do + mlist_free(ms->mlist[i]); + while (i != 0); + } + free(mfn); + return -1; + } + } + fn = mfn; + + while (fn) { + p = strchr(fn, PATHSEP); + if (p) + *p++ = '\0'; + if (*fn == '\0') + break; + file_err = apprentice_1(ms, fn, action); + errs = MAX(errs, file_err); + fn = p; + } + + free(mfn); + + if (errs == -1) { + for (i = 0; i < MAGIC_SETS; i++) { + mlist_free(ms->mlist[i]); + ms->mlist[i] = NULL; + } + file_error(ms, 0, "could not find any valid magic files!"); + return -1; + } + +#if 0 + /* + * Always leave the database loaded + */ + if (action == FILE_LOAD) + return 0; + + for (i = 0; i < MAGIC_SETS; i++) { + mlist_free(ms->mlist[i]); + ms->mlist[i] = NULL; + } +#endif + + switch (action) { + case FILE_LOAD: + case FILE_COMPILE: + case FILE_CHECK: + case FILE_LIST: + return 0; + default: + file_error(ms, 0, "Invalid action %d", action); + return -1; + } +} + +/* + * Compute the real length of a magic expression, for the purposes + * of determining how "strong" a magic expression is (approximating + * how specific its matches are): + * - magic characters count 0 unless escaped. + * - [] expressions count 1 + * - {} expressions count 0 + * - regular characters or escaped magic characters count 1 + * - 0 length expressions count as one + */ +private size_t +nonmagic(const char *str) +{ + const char *p; + size_t rv = 0; + + for (p = str; *p; p++) + switch (*p) { + case '\\': /* Escaped anything counts 1 */ + if (!*++p) + p--; + rv++; + continue; + case '?': /* Magic characters count 0 */ + case '*': + case '.': + case '+': + case '^': + case '$': + continue; + case '[': /* Bracketed expressions count 1 the ']' */ + while (*p && *p != ']') + p++; + p--; + continue; + case '{': /* Braced expressions count 0 */ + while (*p && *p != '}') + p++; + if (!*p) + p--; + continue; + default: /* Anything else counts 1 */ + rv++; + continue; + } + + return rv == 0 ? 1 : rv; /* Return at least 1 */ +} + +/* + * Get weight of this magic entry, for sorting purposes. + */ +private size_t +apprentice_magic_strength(const struct magic *m) +{ +#define MULT 10 + size_t v, val = 2 * MULT; /* baseline strength */ + + switch (m->type) { + case FILE_DEFAULT: /* make sure this sorts last */ + if (m->factor_op != FILE_FACTOR_OP_NONE) + abort(); + return 0; + + case FILE_BYTE: + val += 1 * MULT; + break; + + case FILE_SHORT: + case FILE_LESHORT: + case FILE_BESHORT: + val += 2 * MULT; + break; + + case FILE_LONG: + case FILE_LELONG: + case FILE_BELONG: + case FILE_MELONG: + val += 4 * MULT; + break; + + case FILE_PSTRING: + case FILE_STRING: + val += m->vallen * MULT; + break; + + case FILE_BESTRING16: + case FILE_LESTRING16: + val += m->vallen * MULT / 2; + break; + + case FILE_SEARCH: + val += m->vallen * MAX(MULT / m->vallen, 1); + break; + + case FILE_REGEX: + v = nonmagic(m->value.s); + val += v * MAX(MULT / v, 1); + break; + + case FILE_DATE: + case FILE_LEDATE: + case FILE_BEDATE: + case FILE_MEDATE: + case FILE_LDATE: + case FILE_LELDATE: + case FILE_BELDATE: + case FILE_MELDATE: + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + val += 4 * MULT; + break; + + case FILE_QUAD: + case FILE_BEQUAD: + case FILE_LEQUAD: + case FILE_QDATE: + case FILE_LEQDATE: + case FILE_BEQDATE: + case FILE_QLDATE: + case FILE_LEQLDATE: + case FILE_BEQLDATE: + case FILE_QWDATE: + case FILE_LEQWDATE: + case FILE_BEQWDATE: + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + val += 8 * MULT; + break; + + case FILE_INDIRECT: + case FILE_NAME: + case FILE_USE: + break; + + default: + (void)fprintf(stderr, "Bad type %d\n", m->type); + abort(); + } + + switch (m->reln) { + case 'x': /* matches anything penalize */ + case '!': /* matches almost anything penalize */ + val = 0; + break; + + case '=': /* Exact match, prefer */ + val += MULT; + break; + + case '>': + case '<': /* comparison match reduce strength */ + val -= 2 * MULT; + break; + + case '^': + case '&': /* masking bits, we could count them too */ + val -= MULT; + break; + + default: + (void)fprintf(stderr, "Bad relation %c\n", m->reln); + abort(); + } + + if (val == 0) /* ensure we only return 0 for FILE_DEFAULT */ + val = 1; + + switch (m->factor_op) { + case FILE_FACTOR_OP_NONE: + break; + case FILE_FACTOR_OP_PLUS: + val += m->factor; + break; + case FILE_FACTOR_OP_MINUS: + val -= m->factor; + break; + case FILE_FACTOR_OP_TIMES: + val *= m->factor; + break; + case FILE_FACTOR_OP_DIV: + val /= m->factor; + break; + default: + abort(); + } + + /* + * Magic entries with no description get a bonus because they depend + * on subsequent magic entries to print something. + */ + if (m->desc[0] == '\0') + val++; + return val; +} + +/* + * Sort callback for sorting entries by "strength" (basically length) + */ +private int +apprentice_sort(const void *a, const void *b) +{ + const struct magic_entry *ma = CAST(const struct magic_entry *, a); + const struct magic_entry *mb = CAST(const struct magic_entry *, b); + size_t sa = apprentice_magic_strength(ma->mp); + size_t sb = apprentice_magic_strength(mb->mp); + if (sa == sb) + return 0; + else if (sa > sb) + return -1; + else + return 1; +} + +/* + * Shows sorted patterns list in the order which is used for the matching + */ +private void +apprentice_list(struct mlist *mlist, int mode) +{ + uint32_t magindex = 0; + struct mlist *ml; + for (ml = mlist->next; ml != mlist; ml = ml->next) { + for (magindex = 0; magindex < ml->nmagic; magindex++) { + struct magic *m = &ml->magic[magindex]; + if ((m->flag & mode) != mode) { + /* Skip sub-tests */ + while (magindex + 1 < ml->nmagic && + ml->magic[magindex + 1].cont_level != 0) + ++magindex; + continue; /* Skip to next top-level test*/ + } + + /* + * Try to iterate over the tree until we find item with + * description/mimetype. + */ + while (magindex + 1 < ml->nmagic && + ml->magic[magindex + 1].cont_level != 0 && + *ml->magic[magindex].desc == '\0' && + *ml->magic[magindex].mimetype == '\0') + magindex++; + + printf("Strength = %3" SIZE_T_FORMAT "u : %s [%s]\n", + apprentice_magic_strength(m), + ml->magic[magindex].desc, + ml->magic[magindex].mimetype); + } + } +} + +private void +set_test_type(struct magic *mstart, struct magic *m) +{ + switch (m->type) { + case FILE_BYTE: + case FILE_SHORT: + case FILE_LONG: + case FILE_DATE: + case FILE_BESHORT: + case FILE_BELONG: + case FILE_BEDATE: + case FILE_LESHORT: + case FILE_LELONG: + case FILE_LEDATE: + case FILE_LDATE: + case FILE_BELDATE: + case FILE_LELDATE: + case FILE_MEDATE: + case FILE_MELDATE: + case FILE_MELONG: + case FILE_QUAD: + case FILE_LEQUAD: + case FILE_BEQUAD: + case FILE_QDATE: + case FILE_LEQDATE: + case FILE_BEQDATE: + case FILE_QLDATE: + case FILE_LEQLDATE: + case FILE_BEQLDATE: + case FILE_QWDATE: + case FILE_LEQWDATE: + case FILE_BEQWDATE: + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + mstart->flag |= BINTEST; + break; + case FILE_STRING: + case FILE_PSTRING: + case FILE_BESTRING16: + case FILE_LESTRING16: + /* Allow text overrides */ + if (mstart->str_flags & STRING_TEXTTEST) + mstart->flag |= TEXTTEST; + else + mstart->flag |= BINTEST; + break; + case FILE_REGEX: + case FILE_SEARCH: + /* Check for override */ + if (mstart->str_flags & STRING_BINTEST) + mstart->flag |= BINTEST; + if (mstart->str_flags & STRING_TEXTTEST) + mstart->flag |= TEXTTEST; + + if (mstart->flag & (TEXTTEST|BINTEST)) + break; + + /* binary test if pattern is not text */ + if (file_looks_utf8(m->value.us, (size_t)m->vallen, NULL, + NULL) <= 0) + mstart->flag |= BINTEST; + else + mstart->flag |= TEXTTEST; + break; + case FILE_DEFAULT: + /* can't deduce anything; we shouldn't see this at the + top level anyway */ + break; + case FILE_INVALID: + default: + /* invalid search type, but no need to complain here */ + break; + } +} + +private int +addentry(struct magic_set *ms, struct magic_entry *me, + struct magic_entry_set *mset) +{ + size_t i = me->mp->type == FILE_NAME ? 1 : 0; + if (mset[i].count == mset[i].max) { + struct magic_entry *mp; + + mset[i].max += ALLOC_INCR; + if ((mp = CAST(struct magic_entry *, + realloc(mset[i].me, sizeof(*mp) * mset[i].max))) == + NULL) { + file_oomem(ms, sizeof(*mp) * mset[i].max); + return -1; + } + (void)memset(&mp[mset[i].count], 0, sizeof(*mp) * + ALLOC_INCR); + mset[i].me = mp; + } + mset[i].me[mset[i].count++] = *me; + memset(me, 0, sizeof(*me)); + return 0; +} + +/* + * Load and parse one file. + */ +private void +load_1(struct magic_set *ms, int action, const char *fn, int *errs, + struct magic_entry_set *mset) +{ + size_t lineno = 0, llen = 0; + char *line = NULL; + ssize_t len; + struct magic_entry me; + + FILE *f = fopen(ms->file = fn, "r"); + if (f == NULL) { + if (errno != ENOENT) + file_error(ms, errno, "cannot read magic file `%s'", + fn); + (*errs)++; + return; + } + + memset(&me, 0, sizeof(me)); + /* read and parse this file */ + for (ms->line = 1; (len = getline(&line, &llen, f)) != -1; + ms->line++) { + if (len == 0) /* null line, garbage, etc */ + continue; + if (line[len - 1] == '\n') { + lineno++; + line[len - 1] = '\0'; /* delete newline */ + } + switch (line[0]) { + case '\0': /* empty, do not parse */ + case '#': /* comment, do not parse */ + continue; + case '!': + if (line[1] == ':') { + size_t i; + + for (i = 0; bang[i].name != NULL; i++) { + if ((size_t)(len - 2) > bang[i].len && + memcmp(bang[i].name, line + 2, + bang[i].len) == 0) + break; + } + if (bang[i].name == NULL) { + file_error(ms, 0, + "Unknown !: entry `%s'", line); + (*errs)++; + continue; + } + if (me.mp == NULL) { + file_error(ms, 0, + "No current entry for :!%s type", + bang[i].name); + (*errs)++; + continue; + } + if ((*bang[i].fun)(ms, &me, + line + bang[i].len + 2) != 0) { + (*errs)++; + continue; + } + continue; + } + /*FALLTHROUGH*/ + default: + again: + switch (parse(ms, &me, line, lineno, action)) { + case 0: + continue; + case 1: + (void)addentry(ms, &me, mset); + goto again; + default: + (*errs)++; + break; + } + } + } + if (me.mp) + (void)addentry(ms, &me, mset); + free(line); + (void)fclose(f); +} + +/* + * parse a file or directory of files + * const char *fn: name of magic file or directory + */ +private int +cmpstrp(const void *p1, const void *p2) +{ + return strcmp(*(char *const *)p1, *(char *const *)p2); +} + + +private uint32_t +set_text_binary(struct magic_set *ms, struct magic_entry *me, uint32_t nme, + uint32_t starttest) +{ + static const char text[] = "text"; + static const char binary[] = "binary"; + static const size_t len = sizeof(text); + + uint32_t i = starttest; + + do { + set_test_type(me[starttest].mp, me[i].mp); + if ((ms->flags & MAGIC_DEBUG) == 0) + continue; + (void)fprintf(stderr, "%s%s%s: %s\n", + me[i].mp->mimetype, + me[i].mp->mimetype[0] == '\0' ? "" : "; ", + me[i].mp->desc[0] ? me[i].mp->desc : "(no description)", + me[i].mp->flag & BINTEST ? binary : text); + if (me[i].mp->flag & BINTEST) { + char *p = strstr(me[i].mp->desc, text); + if (p && (p == me[i].mp->desc || + isspace((unsigned char)p[-1])) && + (p + len - me[i].mp->desc == MAXstring + || (p[len] == '\0' || + isspace((unsigned char)p[len])))) + (void)fprintf(stderr, "*** Possible " + "binary test for text type\n"); + } + } while (++i < nme && me[i].mp->cont_level != 0); + return i; +} + +private void +set_last_default(struct magic_set *ms, struct magic_entry *me, uint32_t nme) +{ + uint32_t i; + for (i = 0; i < nme; i++) { + if (me[i].mp->cont_level == 0 && + me[i].mp->type == FILE_DEFAULT) { + while (++i < nme) + if (me[i].mp->cont_level == 0) + break; + if (i != nme) { + /* XXX - Ugh! */ + ms->line = me[i].mp->lineno; + file_magwarn(ms, + "level 0 \"default\" did not sort last"); + } + return; + } + } +} + +private int +coalesce_entries(struct magic_set *ms, struct magic_entry *me, uint32_t nme, + struct magic **ma, uint32_t *nma) +{ + uint32_t i, mentrycount = 0; + size_t slen; + + for (i = 0; i < nme; i++) + mentrycount += me[i].cont_count; + + slen = sizeof(**ma) * mentrycount; + if ((*ma = CAST(struct magic *, malloc(slen))) == NULL) { + file_oomem(ms, slen); + return -1; + } + + mentrycount = 0; + for (i = 0; i < nme; i++) { + (void)memcpy(*ma + mentrycount, me[i].mp, + me[i].cont_count * sizeof(**ma)); + mentrycount += me[i].cont_count; + } + *nma = mentrycount; + return 0; +} + +private void +magic_entry_free(struct magic_entry *me, uint32_t nme) +{ + uint32_t i; + if (me == NULL) + return; + for (i = 0; i < nme; i++) + free(me[i].mp); + free(me); +} + +private struct magic_map * +apprentice_load(struct magic_set *ms, const char *fn, int action) +{ + int errs = 0; + uint32_t i, j; + size_t files = 0, maxfiles = 0; + char **filearr = NULL, *mfn; + struct stat st; + struct magic_map *map; + struct magic_entry_set mset[MAGIC_SETS]; + DIR *dir; + struct dirent *d; + + memset(mset, 0, sizeof(mset)); + ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */ + + + if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) + { + file_oomem(ms, sizeof(*map)); + return NULL; + } + + /* print silly verbose header for USG compat. */ + if (action == FILE_CHECK) + (void)fprintf(stderr, "%s\n", usg_hdr); + + /* load directory or file */ + if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) { + dir = opendir(fn); + if (!dir) { + errs++; + goto out; + } + while ((d = readdir(dir)) != NULL) { + if (asprintf(&mfn, "%s/%s", fn, d->d_name) < 0) { + file_oomem(ms, + strlen(fn) + strlen(d->d_name) + 2); + errs++; + closedir(dir); + goto out; + } + if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) { + free(mfn); + continue; + } + if (files >= maxfiles) { + size_t mlen; + maxfiles = (maxfiles + 1) * 2; + mlen = maxfiles * sizeof(*filearr); + if ((filearr = CAST(char **, + realloc(filearr, mlen))) == NULL) { + file_oomem(ms, mlen); + free(mfn); + closedir(dir); + errs++; + goto out; + } + } + filearr[files++] = mfn; + } + closedir(dir); + qsort(filearr, files, sizeof(*filearr), cmpstrp); + for (i = 0; i < files; i++) { + load_1(ms, action, filearr[i], &errs, mset); + free(filearr[i]); + } + free(filearr); + } else + load_1(ms, action, fn, &errs, mset); + if (errs) + goto out; + + for (j = 0; j < MAGIC_SETS; j++) { + /* Set types of tests */ + for (i = 0; i < mset[j].count; ) { + if (mset[j].me[i].mp->cont_level != 0) { + i++; + continue; + } + i = set_text_binary(ms, mset[j].me, mset[j].count, i); + } + qsort(mset[j].me, mset[j].count, sizeof(*mset[j].me), + apprentice_sort); + + /* + * Make sure that any level 0 "default" line is last + * (if one exists). + */ + set_last_default(ms, mset[j].me, mset[j].count); + + /* coalesce per file arrays into a single one */ + if (coalesce_entries(ms, mset[j].me, mset[j].count, + &map->magic[j], &map->nmagic[j]) == -1) { + errs++; + goto out; + } + } + +out: + for (j = 0; j < MAGIC_SETS; j++) + magic_entry_free(mset[j].me, mset[j].count); + + if (errs) { + apprentice_unmap(map); + return NULL; + } + return map; +} + +/* + * extend the sign bit if the comparison is to be signed + */ +protected uint64_t +file_signextend(struct magic_set *ms, struct magic *m, uint64_t v) +{ + if (!(m->flag & UNSIGNED)) { + switch(m->type) { + /* + * Do not remove the casts below. They are + * vital. When later compared with the data, + * the sign extension must have happened. + */ + case FILE_BYTE: + v = (char) v; + break; + case FILE_SHORT: + case FILE_BESHORT: + case FILE_LESHORT: + v = (short) v; + break; + case FILE_DATE: + case FILE_BEDATE: + case FILE_LEDATE: + case FILE_MEDATE: + case FILE_LDATE: + case FILE_BELDATE: + case FILE_LELDATE: + case FILE_MELDATE: + case FILE_LONG: + case FILE_BELONG: + case FILE_LELONG: + case FILE_MELONG: + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + v = (int32_t) v; + break; + case FILE_QUAD: + case FILE_BEQUAD: + case FILE_LEQUAD: + case FILE_QDATE: + case FILE_QLDATE: + case FILE_QWDATE: + case FILE_BEQDATE: + case FILE_BEQLDATE: + case FILE_BEQWDATE: + case FILE_LEQDATE: + case FILE_LEQLDATE: + case FILE_LEQWDATE: + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + v = (int64_t) v; + break; + case FILE_STRING: + case FILE_PSTRING: + case FILE_BESTRING16: + case FILE_LESTRING16: + case FILE_REGEX: + case FILE_SEARCH: + case FILE_DEFAULT: + case FILE_INDIRECT: + case FILE_NAME: + case FILE_USE: + case FILE_CLEAR: + break; + default: + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "cannot happen: m->type=%d\n", + m->type); + return ~0U; + } + } + return v; +} + +private int +string_modifier_check(struct magic_set *ms, struct magic *m) +{ + if ((ms->flags & MAGIC_CHECK) == 0) + return 0; + + if ((m->type != FILE_REGEX || (m->str_flags & REGEX_LINE_COUNT) == 0) && + (m->type != FILE_PSTRING && (m->str_flags & PSTRING_LEN) != 0)) { + file_magwarn(ms, + "'/BHhLl' modifiers are only allowed for pascal strings\n"); + return -1; + } + switch (m->type) { + case FILE_BESTRING16: + case FILE_LESTRING16: + if (m->str_flags != 0) { + file_magwarn(ms, + "no modifiers allowed for 16-bit strings\n"); + return -1; + } + break; + case FILE_STRING: + case FILE_PSTRING: + if ((m->str_flags & REGEX_OFFSET_START) != 0) { + file_magwarn(ms, + "'/%c' only allowed on regex and search\n", + CHAR_REGEX_OFFSET_START); + return -1; + } + break; + case FILE_SEARCH: + if (m->str_range == 0) { + file_magwarn(ms, + "missing range; defaulting to %d\n", + STRING_DEFAULT_RANGE); + m->str_range = STRING_DEFAULT_RANGE; + return -1; + } + break; + case FILE_REGEX: + if ((m->str_flags & STRING_COMPACT_WHITESPACE) != 0) { + file_magwarn(ms, "'/%c' not allowed on regex\n", + CHAR_COMPACT_WHITESPACE); + return -1; + } + if ((m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) != 0) { + file_magwarn(ms, "'/%c' not allowed on regex\n", + CHAR_COMPACT_OPTIONAL_WHITESPACE); + return -1; + } + break; + default: + file_magwarn(ms, "coding error: m->type=%d\n", + m->type); + return -1; + } + return 0; +} + +private int +get_op(char c) +{ + switch (c) { + case '&': + return FILE_OPAND; + case '|': + return FILE_OPOR; + case '^': + return FILE_OPXOR; + case '+': + return FILE_OPADD; + case '-': + return FILE_OPMINUS; + case '*': + return FILE_OPMULTIPLY; + case '/': + return FILE_OPDIVIDE; + case '%': + return FILE_OPMODULO; + default: + return -1; + } +} + +#ifdef ENABLE_CONDITIONALS +private int +get_cond(const char *l, const char **t) +{ + static const struct cond_tbl_s { + char name[8]; + size_t len; + int cond; + } cond_tbl[] = { + { "if", 2, COND_IF }, + { "elif", 4, COND_ELIF }, + { "else", 4, COND_ELSE }, + { "", 0, COND_NONE }, + }; + const struct cond_tbl_s *p; + + for (p = cond_tbl; p->len; p++) { + if (strncmp(l, p->name, p->len) == 0 && + isspace((unsigned char)l[p->len])) { + if (t) + *t = l + p->len; + break; + } + } + return p->cond; +} + +private int +check_cond(struct magic_set *ms, int cond, uint32_t cont_level) +{ + int last_cond; + last_cond = ms->c.li[cont_level].last_cond; + + switch (cond) { + case COND_IF: + if (last_cond != COND_NONE && last_cond != COND_ELIF) { + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "syntax error: `if'"); + return -1; + } + last_cond = COND_IF; + break; + + case COND_ELIF: + if (last_cond != COND_IF && last_cond != COND_ELIF) { + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "syntax error: `elif'"); + return -1; + } + last_cond = COND_ELIF; + break; + + case COND_ELSE: + if (last_cond != COND_IF && last_cond != COND_ELIF) { + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "syntax error: `else'"); + return -1; + } + last_cond = COND_NONE; + break; + + case COND_NONE: + last_cond = COND_NONE; + break; + } + + ms->c.li[cont_level].last_cond = last_cond; + return 0; +} +#endif /* ENABLE_CONDITIONALS */ + +/* + * parse one line from magic file, put into magic[index++] if valid + */ +private int +parse(struct magic_set *ms, struct magic_entry *me, const char *line, + size_t lineno, int action) +{ +#ifdef ENABLE_CONDITIONALS + static uint32_t last_cont_level = 0; +#endif + size_t i; + struct magic *m; + const char *l = line; + char *t; + int op; + uint32_t cont_level; + int32_t diff; + + cont_level = 0; + + /* + * Parse the offset. + */ + while (*l == '>') { + ++l; /* step over */ + cont_level++; + } +#ifdef ENABLE_CONDITIONALS + if (cont_level == 0 || cont_level > last_cont_level) + if (file_check_mem(ms, cont_level) == -1) + return -1; + last_cont_level = cont_level; +#endif + if (cont_level != 0) { + if (me->mp == NULL) { + file_magerror(ms, "No current entry for continuation"); + return -1; + } + if (me->cont_count == 0) { + file_magerror(ms, "Continuations present with 0 count"); + return -1; + } + m = &me->mp[me->cont_count - 1]; + diff = (int32_t)cont_level - (int32_t)m->cont_level; + if (diff > 1) + file_magwarn(ms, "New continuation level %u is more " + "than one larger than current level %u", cont_level, + m->cont_level); + if (me->cont_count == me->max_count) { + struct magic *nm; + size_t cnt = me->max_count + ALLOC_CHUNK; + if ((nm = CAST(struct magic *, realloc(me->mp, + sizeof(*nm) * cnt))) == NULL) { + file_oomem(ms, sizeof(*nm) * cnt); + return -1; + } + me->mp = m = nm; + me->max_count = CAST(uint32_t, cnt); + } + m = &me->mp[me->cont_count++]; + (void)memset(m, 0, sizeof(*m)); + m->cont_level = cont_level; + } else { + static const size_t len = sizeof(*m) * ALLOC_CHUNK; + if (me->mp != NULL) + return 1; + if ((m = CAST(struct magic *, malloc(len))) == NULL) { + file_oomem(ms, len); + return -1; + } + me->mp = m; + me->max_count = ALLOC_CHUNK; + (void)memset(m, 0, sizeof(*m)); + m->factor_op = FILE_FACTOR_OP_NONE; + m->cont_level = 0; + me->cont_count = 1; + } + m->lineno = CAST(uint32_t, lineno); + + if (*l == '&') { /* m->cont_level == 0 checked below. */ + ++l; /* step over */ + m->flag |= OFFADD; + } + if (*l == '(') { + ++l; /* step over */ + m->flag |= INDIR; + if (m->flag & OFFADD) + m->flag = (m->flag & ~OFFADD) | INDIROFFADD; + + if (*l == '&') { /* m->cont_level == 0 checked below */ + ++l; /* step over */ + m->flag |= OFFADD; + } + } + /* Indirect offsets are not valid at level 0. */ + if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD))) + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "relative offset at level 0"); + + /* get offset, then skip over it */ + m->offset = (uint32_t)strtoul(l, &t, 0); + if (l == t) + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "offset `%s' invalid", l); + l = t; + + if (m->flag & INDIR) { + m->in_type = FILE_LONG; + m->in_offset = 0; + /* + * read [.lbs][+-]nnnnn) + */ + if (*l == '.') { + l++; + switch (*l) { + case 'l': + m->in_type = FILE_LELONG; + break; + case 'L': + m->in_type = FILE_BELONG; + break; + case 'm': + m->in_type = FILE_MELONG; + break; + case 'h': + case 's': + m->in_type = FILE_LESHORT; + break; + case 'H': + case 'S': + m->in_type = FILE_BESHORT; + break; + case 'c': + case 'b': + case 'C': + case 'B': + m->in_type = FILE_BYTE; + break; + case 'e': + case 'f': + case 'g': + m->in_type = FILE_LEDOUBLE; + break; + case 'E': + case 'F': + case 'G': + m->in_type = FILE_BEDOUBLE; + break; + case 'i': + m->in_type = FILE_LEID3; + break; + case 'I': + m->in_type = FILE_BEID3; + break; + default: + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, + "indirect offset type `%c' invalid", + *l); + break; + } + l++; + } + + m->in_op = 0; + if (*l == '~') { + m->in_op |= FILE_OPINVERSE; + l++; + } + if ((op = get_op(*l)) != -1) { + m->in_op |= op; + l++; + } + if (*l == '(') { + m->in_op |= FILE_OPINDIRECT; + l++; + } + if (isdigit((unsigned char)*l) || *l == '-') { + m->in_offset = (int32_t)strtol(l, &t, 0); + if (l == t) + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, + "in_offset `%s' invalid", l); + l = t; + } + if (*l++ != ')' || + ((m->in_op & FILE_OPINDIRECT) && *l++ != ')')) + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, + "missing ')' in indirect offset"); + } + EATAB; + +#ifdef ENABLE_CONDITIONALS + m->cond = get_cond(l, &l); + if (check_cond(ms, m->cond, cont_level) == -1) + return -1; + + EATAB; +#endif + + /* + * Parse the type. + */ + if (*l == 'u') { + /* + * Try it as a keyword type prefixed by "u"; match what + * follows the "u". If that fails, try it as an SUS + * integer type. + */ + m->type = get_type(type_tbl, l + 1, &l); + if (m->type == FILE_INVALID) { + /* + * Not a keyword type; parse it as an SUS type, + * 'u' possibly followed by a number or C/S/L. + */ + m->type = get_standard_integer_type(l, &l); + } + /* It's unsigned. */ + if (m->type != FILE_INVALID) + m->flag |= UNSIGNED; + } else { + /* + * Try it as a keyword type. If that fails, try it as + * an SUS integer type if it begins with "d" or as an + * SUS string type if it begins with "s". In any case, + * it's not unsigned. + */ + m->type = get_type(type_tbl, l, &l); + if (m->type == FILE_INVALID) { + /* + * Not a keyword type; parse it as an SUS type, + * either 'd' possibly followed by a number or + * C/S/L, or just 's'. + */ + if (*l == 'd') + m->type = get_standard_integer_type(l, &l); + else if (*l == 's' && !isalpha((unsigned char)l[1])) { + m->type = FILE_STRING; + ++l; + } + } + } + + if (m->type == FILE_INVALID) { + /* Not found - try it as a special keyword. */ + m->type = get_type(special_tbl, l, &l); + } + + if (m->type == FILE_INVALID) { + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "type `%s' invalid", l); + return -1; + } + + /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */ + /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */ + + m->mask_op = 0; + if (*l == '~') { + if (!IS_STRING(m->type)) + m->mask_op |= FILE_OPINVERSE; + else if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "'~' invalid for string types"); + ++l; + } + m->str_range = 0; + m->str_flags = m->type == FILE_PSTRING ? PSTRING_1_LE : 0; + if ((op = get_op(*l)) != -1) { + if (!IS_STRING(m->type)) { + uint64_t val; + ++l; + m->mask_op |= op; + val = (uint64_t)strtoull(l, &t, 0); + l = t; + m->num_mask = file_signextend(ms, m, val); + eatsize(&l); + } + else if (op == FILE_OPDIVIDE) { + int have_range = 0; + while (!isspace((unsigned char)*++l)) { + switch (*l) { + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': + if (have_range && + (ms->flags & MAGIC_CHECK)) + file_magwarn(ms, + "multiple ranges"); + have_range = 1; + m->str_range = CAST(uint32_t, + strtoul(l, &t, 0)); + if (m->str_range == 0) + file_magwarn(ms, + "zero range"); + l = t - 1; + break; + case CHAR_COMPACT_WHITESPACE: + m->str_flags |= + STRING_COMPACT_WHITESPACE; + break; + case CHAR_COMPACT_OPTIONAL_WHITESPACE: + m->str_flags |= + STRING_COMPACT_OPTIONAL_WHITESPACE; + break; + case CHAR_IGNORE_LOWERCASE: + m->str_flags |= STRING_IGNORE_LOWERCASE; + break; + case CHAR_IGNORE_UPPERCASE: + m->str_flags |= STRING_IGNORE_UPPERCASE; + break; + case CHAR_REGEX_OFFSET_START: + m->str_flags |= REGEX_OFFSET_START; + break; + case CHAR_BINTEST: + m->str_flags |= STRING_BINTEST; + break; + case CHAR_TEXTTEST: + m->str_flags |= STRING_TEXTTEST; + break; + case CHAR_TRIM: + m->str_flags |= STRING_TRIM; + break; + case CHAR_PSTRING_1_LE: + if (m->type != FILE_PSTRING) + goto bad; + m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_1_LE; + break; + case CHAR_PSTRING_2_BE: + if (m->type != FILE_PSTRING) + goto bad; + m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_2_BE; + break; + case CHAR_PSTRING_2_LE: + if (m->type != FILE_PSTRING) + goto bad; + m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_2_LE; + break; + case CHAR_PSTRING_4_BE: + if (m->type != FILE_PSTRING) + goto bad; + m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_4_BE; + break; + case CHAR_PSTRING_4_LE: + switch (m->type) { + case FILE_PSTRING: + case FILE_REGEX: + break; + default: + goto bad; + } + m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_4_LE; + break; + case CHAR_PSTRING_LENGTH_INCLUDES_ITSELF: + if (m->type != FILE_PSTRING) + goto bad; + m->str_flags |= PSTRING_LENGTH_INCLUDES_ITSELF; + break; + default: + bad: + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, + "string extension `%c' " + "invalid", *l); + return -1; + } + /* allow multiple '/' for readability */ + if (l[1] == '/' && + !isspace((unsigned char)l[2])) + l++; + } + if (string_modifier_check(ms, m) == -1) + return -1; + } + else { + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "invalid string op: %c", *t); + return -1; + } + } + /* + * We used to set mask to all 1's here, instead let's just not do + * anything if mask = 0 (unless you have a better idea) + */ + EATAB; + + switch (*l) { + case '>': + case '<': + m->reln = *l; + ++l; + if (*l == '=') { + if (ms->flags & MAGIC_CHECK) { + file_magwarn(ms, "%c= not supported", + m->reln); + return -1; + } + ++l; + } + break; + /* Old-style anding: "0 byte &0x80 dynamically linked" */ + case '&': + case '^': + case '=': + m->reln = *l; + ++l; + if (*l == '=') { + /* HP compat: ignore &= etc. */ + ++l; + } + break; + case '!': + m->reln = *l; + ++l; + break; + default: + m->reln = '='; /* the default relation */ + if (*l == 'x' && ((isascii((unsigned char)l[1]) && + isspace((unsigned char)l[1])) || !l[1])) { + m->reln = *l; + ++l; + } + break; + } + /* + * Grab the value part, except for an 'x' reln. + */ + if (m->reln != 'x' && getvalue(ms, m, &l, action)) + return -1; + + /* + * TODO finish this macro and start using it! + * #define offsetcheck {if (offset > HOWMANY-1) + * magwarn("offset too big"); } + */ + + /* + * Now get last part - the description + */ + EATAB; + if (l[0] == '\b') { + ++l; + m->flag |= NOSPACE; + } else if ((l[0] == '\\') && (l[1] == 'b')) { + ++l; + ++l; + m->flag |= NOSPACE; + } + for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); ) + continue; + if (i == sizeof(m->desc)) { + m->desc[sizeof(m->desc) - 1] = '\0'; + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "description `%s' truncated", m->desc); + } + + /* + * We only do this check while compiling, or if any of the magic + * files were not compiled. + */ + if (ms->flags & MAGIC_CHECK) { + if (check_format(ms, m) == -1) + return -1; + } +#ifndef COMPILE_ONLY + if (action == FILE_CHECK) { + file_mdump(m); + } +#endif + m->mimetype[0] = '\0'; /* initialise MIME type to none */ + return 0; +} + +/* + * parse a STRENGTH annotation line from magic file, put into magic[index - 1] + * if valid + */ +private int +parse_strength(struct magic_set *ms, struct magic_entry *me, const char *line) +{ + const char *l = line; + char *el; + unsigned long factor; + struct magic *m = &me->mp[0]; + + if (m->factor_op != FILE_FACTOR_OP_NONE) { + file_magwarn(ms, + "Current entry already has a strength type: %c %d", + m->factor_op, m->factor); + return -1; + } + if (m->type == FILE_NAME) { + file_magwarn(ms, "%s: Strength setting is not supported in " + "\"name\" magic entries", m->value.s); + return -1; + } + EATAB; + switch (*l) { + case FILE_FACTOR_OP_NONE: + case FILE_FACTOR_OP_PLUS: + case FILE_FACTOR_OP_MINUS: + case FILE_FACTOR_OP_TIMES: + case FILE_FACTOR_OP_DIV: + m->factor_op = *l++; + break; + default: + file_magwarn(ms, "Unknown factor op `%c'", *l); + return -1; + } + EATAB; + factor = strtoul(l, &el, 0); + if (factor > 255) { + file_magwarn(ms, "Too large factor `%lu'", factor); + goto out; + } + if (*el && !isspace((unsigned char)*el)) { + file_magwarn(ms, "Bad factor `%s'", l); + goto out; + } + m->factor = (uint8_t)factor; + if (m->factor == 0 && m->factor_op == FILE_FACTOR_OP_DIV) { + file_magwarn(ms, "Cannot have factor op `%c' and factor %u", + m->factor_op, m->factor); + goto out; + } + return 0; +out: + m->factor_op = FILE_FACTOR_OP_NONE; + m->factor = 0; + return -1; +} + +private int +parse_extra(struct magic_set *ms, struct magic_entry *me, const char *line, + off_t off, size_t len, const char *name, int nt) +{ + size_t i; + const char *l = line; + struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1]; + char *buf = (char *)m + off; + + if (buf[0] != '\0') { + len = nt ? strlen(buf) : len; + file_magwarn(ms, "Current entry already has a %s type " + "`%.*s', new type `%s'", name, (int)len, buf, l); + return -1; + } + + if (*m->desc == '\0') { + file_magwarn(ms, "Current entry does not yet have a " + "description for adding a %s type", name); + return -1; + } + + EATAB; + for (i = 0; *l && ((isascii((unsigned char)*l) && + isalnum((unsigned char)*l)) || strchr("-+/.", *l)) && + i < len; buf[i++] = *l++) + continue; + + if (i == len && *l) { + if (nt) + buf[len - 1] = '\0'; + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "%s type `%s' truncated %" + SIZE_T_FORMAT "u", name, line, i); + } else { + if (nt) + buf[i] = '\0'; + } + + if (i > 0) + return 0; + else + return -1; +} + +/* + * Parse an Apple CREATOR/TYPE annotation from magic file and put it into + * magic[index - 1] + */ +private int +parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line) +{ + struct magic *m = &me->mp[0]; + + return parse_extra(ms, me, line, offsetof(struct magic, apple), + sizeof(m->apple), "APPLE", 0); +} + +/* + * parse a MIME annotation line from magic file, put into magic[index - 1] + * if valid + */ +private int +parse_mime(struct magic_set *ms, struct magic_entry *me, const char *line) +{ + struct magic *m = &me->mp[0]; + + return parse_extra(ms, me, line, offsetof(struct magic, mimetype), + sizeof(m->mimetype), "MIME", 1); +} + +private int +check_format_type(const char *ptr, int type) +{ + int quad = 0, h; + if (*ptr == '\0') { + /* Missing format string; bad */ + return -1; + } + + switch (file_formats[type]) { + case FILE_FMT_QUAD: + quad = 1; + /*FALLTHROUGH*/ + case FILE_FMT_NUM: + if (quad == 0) { + switch (type) { + case FILE_BYTE: + h = 2; + break; + case FILE_SHORT: + case FILE_BESHORT: + case FILE_LESHORT: + h = 1; + break; + case FILE_LONG: + case FILE_BELONG: + case FILE_LELONG: + case FILE_MELONG: + case FILE_LEID3: + case FILE_BEID3: + case FILE_INDIRECT: + h = 0; + break; + default: + abort(); + } + } else + h = 0; + if (*ptr == '-') + ptr++; + if (*ptr == '.') + ptr++; + while (isdigit((unsigned char)*ptr)) ptr++; + if (*ptr == '.') + ptr++; + while (isdigit((unsigned char)*ptr)) ptr++; + if (quad) { + if (*ptr++ != 'l') + return -1; + if (*ptr++ != 'l') + return -1; + } + + switch (*ptr++) { +#ifdef STRICT_FORMAT /* "long" formats are int formats for us */ + /* so don't accept the 'l' modifier */ + case 'l': + switch (*ptr++) { + case 'i': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + return h != 0 ? -1 : 0; + default: + return -1; + } + + /* + * Don't accept h and hh modifiers. They make writing + * magic entries more complicated, for very little benefit + */ + case 'h': + if (h-- <= 0) + return -1; + switch (*ptr++) { + case 'h': + if (h-- <= 0) + return -1; + switch (*ptr++) { + case 'i': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + return 0; + default: + return -1; + } + case 'i': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + return h != 0 ? -1 : 0; + default: + return -1; + } +#endif + case 'c': + return h != 2 ? -1 : 0; + case 'i': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': +#ifdef STRICT_FORMAT + return h != 0 ? -1 : 0; +#else + return 0; +#endif + default: + return -1; + } + + case FILE_FMT_FLOAT: + case FILE_FMT_DOUBLE: + if (*ptr == '-') + ptr++; + if (*ptr == '.') + ptr++; + while (isdigit((unsigned char)*ptr)) ptr++; + if (*ptr == '.') + ptr++; + while (isdigit((unsigned char)*ptr)) ptr++; + + switch (*ptr++) { + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + return 0; + + default: + return -1; + } + + + case FILE_FMT_STR: + if (*ptr == '-') + ptr++; + while (isdigit((unsigned char )*ptr)) + ptr++; + if (*ptr == '.') { + ptr++; + while (isdigit((unsigned char )*ptr)) + ptr++; + } + + switch (*ptr++) { + case 's': + return 0; + default: + return -1; + } + + default: + /* internal error */ + abort(); + } + /*NOTREACHED*/ + return -1; +} + +/* + * Check that the optional printf format in description matches + * the type of the magic. + */ +private int +check_format(struct magic_set *ms, struct magic *m) +{ + char *ptr; + + for (ptr = m->desc; *ptr; ptr++) + if (*ptr == '%') + break; + if (*ptr == '\0') { + /* No format string; ok */ + return 1; + } + + assert(file_nformats == file_nnames); + + if (m->type >= file_nformats) { + file_magwarn(ms, "Internal error inconsistency between " + "m->type and format strings"); + return -1; + } + if (file_formats[m->type] == FILE_FMT_NONE) { + file_magwarn(ms, "No format string for `%s' with description " + "`%s'", m->desc, file_names[m->type]); + return -1; + } + + ptr++; + if (check_format_type(ptr, m->type) == -1) { + /* + * TODO: this error message is unhelpful if the format + * string is not one character long + */ + file_magwarn(ms, "Printf format `%c' is not valid for type " + "`%s' in description `%s'", *ptr ? *ptr : '?', + file_names[m->type], m->desc); + return -1; + } + + for (; *ptr; ptr++) { + if (*ptr == '%') { + file_magwarn(ms, + "Too many format strings (should have at most one) " + "for `%s' with description `%s'", + file_names[m->type], m->desc); + return -1; + } + } + return 0; +} + +/* + * Read a numeric value from a pointer, into the value union of a magic + * pointer, according to the magic type. Update the string pointer to point + * just after the number read. Return 0 for success, non-zero for failure. + */ +private int +getvalue(struct magic_set *ms, struct magic *m, const char **p, int action) +{ + switch (m->type) { + case FILE_BESTRING16: + case FILE_LESTRING16: + case FILE_STRING: + case FILE_PSTRING: + case FILE_REGEX: + case FILE_SEARCH: + case FILE_NAME: + case FILE_USE: + *p = getstr(ms, m, *p, action == FILE_COMPILE); + if (*p == NULL) { + if (ms->flags & MAGIC_CHECK) + file_magwarn(ms, "cannot get string from `%s'", + m->value.s); + return -1; + } + if (m->type == FILE_REGEX) { + file_regex_t rx; + int rc = file_regcomp(&rx, m->value.s, REG_EXTENDED); + if (rc) { + if (ms->flags & MAGIC_CHECK) + file_regerror(&rx, rc, ms); + } + file_regfree(&rx); + return rc ? -1 : 0; + } + return 0; + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + if (m->reln != 'x') { + char *ep; +#ifdef HAVE_STRTOF + m->value.f = strtof(*p, &ep); +#else + m->value.f = (float)strtod(*p, &ep); +#endif + *p = ep; + } + return 0; + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + if (m->reln != 'x') { + char *ep; + m->value.d = strtod(*p, &ep); + *p = ep; + } + return 0; + default: + if (m->reln != 'x') { + char *ep; + m->value.q = file_signextend(ms, m, + (uint64_t)strtoull(*p, &ep, 0)); + *p = ep; + eatsize(p); + } + return 0; + } +} + +/* + * Convert a string containing C character escapes. Stop at an unescaped + * space or tab. + * Copy the converted version to "m->value.s", and the length in m->vallen. + * Return updated scan pointer as function result. Warn if set. + */ +private const char * +getstr(struct magic_set *ms, struct magic *m, const char *s, int warn) +{ + const char *origs = s; + char *p = m->value.s; + size_t plen = sizeof(m->value.s); + char *origp = p; + char *pmax = p + plen - 1; + int c; + int val; + + while ((c = *s++) != '\0') { + if (isspace((unsigned char) c)) + break; + if (p >= pmax) { + file_error(ms, 0, "string too long: `%s'", origs); + return NULL; + } + if (c == '\\') { + switch(c = *s++) { + + case '\0': + if (warn) + file_magwarn(ms, "incomplete escape"); + goto out; + + case '\t': + if (warn) { + file_magwarn(ms, + "escaped tab found, use \\t instead"); + warn = 0; /* already did */ + } + /*FALLTHROUGH*/ + default: + if (warn) { + if (isprint((unsigned char)c)) { + /* Allow escaping of + * ``relations'' */ + if (strchr("<>&^=!", c) == NULL + && (m->type != FILE_REGEX || + strchr("[]().*?^$|{}", c) + == NULL)) { + file_magwarn(ms, "no " + "need to escape " + "`%c'", c); + } + } else { + file_magwarn(ms, + "unknown escape sequence: " + "\\%03o", c); + } + } + /*FALLTHROUGH*/ + /* space, perhaps force people to use \040? */ + case ' ': +#if 0 + /* + * Other things people escape, but shouldn't need to, + * so we disallow them + */ + case '\'': + case '"': + case '?': +#endif + /* Relations */ + case '>': + case '<': + case '&': + case '^': + case '=': + case '!': + /* and baskslash itself */ + case '\\': + *p++ = (char) c; + break; + + case 'a': + *p++ = '\a'; + break; + + case 'b': + *p++ = '\b'; + break; + + case 'f': + *p++ = '\f'; + break; + + case 'n': + *p++ = '\n'; + break; + + case 'r': + *p++ = '\r'; + break; + + case 't': + *p++ = '\t'; + break; + + case 'v': + *p++ = '\v'; + break; + + /* \ and up to 3 octal digits */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = c - '0'; + c = *s++; /* try for 2 */ + if (c >= '0' && c <= '7') { + val = (val << 3) | (c - '0'); + c = *s++; /* try for 3 */ + if (c >= '0' && c <= '7') + val = (val << 3) | (c-'0'); + else + --s; + } + else + --s; + *p++ = (char)val; + break; + + /* \x and up to 2 hex digits */ + case 'x': + val = 'x'; /* Default if no digits */ + c = hextoint(*s++); /* Get next char */ + if (c >= 0) { + val = c; + c = hextoint(*s++); + if (c >= 0) + val = (val << 4) + c; + else + --s; + } else + --s; + *p++ = (char)val; + break; + } + } else + *p++ = (char)c; + } +out: + *p = '\0'; + m->vallen = CAST(unsigned char, (p - origp)); + if (m->type == FILE_PSTRING) + m->vallen += (unsigned char)file_pstring_length_size(m); + return s; +} + + +/* Single hex char to int; -1 if not a hex char. */ +private int +hextoint(int c) +{ + if (!isascii((unsigned char) c)) + return -1; + if (isdigit((unsigned char) c)) + return c - '0'; + if ((c >= 'a') && (c <= 'f')) + return c + 10 - 'a'; + if (( c>= 'A') && (c <= 'F')) + return c + 10 - 'A'; + return -1; +} + + +/* + * Print a string containing C character escapes. + */ +protected void +file_showstr(FILE *fp, const char *s, size_t len) +{ + char c; + + for (;;) { + if (len == ~0U) { + c = *s++; + if (c == '\0') + break; + } + else { + if (len-- == 0) + break; + c = *s++; + } + if (c >= 040 && c <= 0176) /* TODO isprint && !iscntrl */ + (void) fputc(c, fp); + else { + (void) fputc('\\', fp); + switch (c) { + case '\a': + (void) fputc('a', fp); + break; + + case '\b': + (void) fputc('b', fp); + break; + + case '\f': + (void) fputc('f', fp); + break; + + case '\n': + (void) fputc('n', fp); + break; + + case '\r': + (void) fputc('r', fp); + break; + + case '\t': + (void) fputc('t', fp); + break; + + case '\v': + (void) fputc('v', fp); + break; + + default: + (void) fprintf(fp, "%.3o", c & 0377); + break; + } + } + } +} + +/* + * eatsize(): Eat the size spec from a number [eg. 10UL] + */ +private void +eatsize(const char **p) +{ + const char *l = *p; + + if (LOWCASE(*l) == 'u') + l++; + + switch (LOWCASE(*l)) { + case 'l': /* long */ + case 's': /* short */ + case 'h': /* short */ + case 'b': /* char/byte */ + case 'c': /* char/byte */ + l++; + /*FALLTHROUGH*/ + default: + break; + } + + *p = l; +} + +/* + * handle a compiled file. + */ + +private struct magic_map * +apprentice_map(struct magic_set *ms, const char *fn) +{ + int fd; + struct stat st; + uint32_t *ptr; + uint32_t version, entries, nentries; + int needsbyteswap; + char *dbname = NULL; + struct magic_map *map; + size_t i; + + fd = -1; + if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) { + file_oomem(ms, sizeof(*map)); + goto error; + } + + dbname = mkdbname(ms, fn, 0); + if (dbname == NULL) + goto error; + + if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1) + goto error; + + if (fstat(fd, &st) == -1) { + file_error(ms, errno, "cannot stat `%s'", dbname); + goto error; + } + if (st.st_size < 8 || st.st_size > MAXMAGIC_SIZE) { + file_error(ms, 0, "file `%s' is too %s", dbname, + st.st_size < 8 ? "small" : "large"); + goto error; + } + + map->len = (size_t)st.st_size; +#ifdef QUICK + if ((map->p = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) { + file_error(ms, errno, "cannot map `%s'", dbname); + goto error; + } +#else + if ((map->p = CAST(void *, malloc(map->len))) == NULL) { + file_oomem(ms, map->len); + goto error; + } + if (read(fd, map->p, map->len) != (ssize_t)map->len) { + file_badread(ms); + goto error; + } + map->len = 0; +#define RET 1 +#endif + (void)close(fd); + fd = -1; + ptr = CAST(uint32_t *, map->p); + if (*ptr != MAGICNO) { + if (swap4(*ptr) != MAGICNO) { + file_error(ms, 0, "bad magic in `%s'", dbname); + goto error; + } + needsbyteswap = 1; + } else + needsbyteswap = 0; + if (needsbyteswap) + version = swap4(ptr[1]); + else + version = ptr[1]; + if (version != VERSIONNO) { + file_error(ms, 0, "File %s supports only version %d magic " + "files. `%s' is version %d", VERSION, + VERSIONNO, dbname, version); + goto error; + } + entries = (uint32_t)(st.st_size / sizeof(struct magic)); + if ((off_t)(entries * sizeof(struct magic)) != st.st_size) { + file_error(ms, 0, "Size of `%s' %" INT64_T_FORMAT "u is not " + "a multiple of %" SIZE_T_FORMAT "u", + dbname, (unsigned long long)st.st_size, + sizeof(struct magic)); + goto error; + } + map->magic[0] = CAST(struct magic *, map->p) + 1; + nentries = 0; + for (i = 0; i < MAGIC_SETS; i++) { + if (needsbyteswap) + map->nmagic[i] = swap4(ptr[i + 2]); + else + map->nmagic[i] = ptr[i + 2]; + if (i != MAGIC_SETS - 1) + map->magic[i + 1] = map->magic[i] + map->nmagic[i]; + nentries += map->nmagic[i]; + } + if (entries != nentries + 1) { + file_error(ms, 0, "Inconsistent entries in `%s' %u != %u", + dbname, entries, nentries + 1); + goto error; + } + if (needsbyteswap) + for (i = 0; i < MAGIC_SETS; i++) + byteswap(map->magic[i], map->nmagic[i]); + free(dbname); + return map; + +error: + if (fd != -1) + (void)close(fd); + apprentice_unmap(map); + free(dbname); + return NULL; +} + +/* + * handle an mmaped file. + */ +private int +apprentice_compile(struct magic_set *ms, struct magic_map *map, const char *fn) +{ + static const size_t nm = sizeof(*map->nmagic) * MAGIC_SETS; + static const size_t m = sizeof(**map->magic); + int fd = -1; + size_t len; + char *dbname; + int rv = -1; + uint32_t i; + union { + struct magic m; + uint32_t h[2 + MAGIC_SETS]; + } hdr; + + dbname = mkdbname(ms, fn, 1); + + if (dbname == NULL) + goto out; + + if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) + { + file_error(ms, errno, "cannot open `%s'", dbname); + goto out; + } + memset(&hdr, 0, sizeof(hdr)); + hdr.h[0] = MAGICNO; + hdr.h[1] = VERSIONNO; + memcpy(hdr.h + 2, map->nmagic, nm); + + if (write(fd, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) { + file_error(ms, errno, "error writing `%s'", dbname); + goto out; + } + + for (i = 0; i < MAGIC_SETS; i++) { + len = m * map->nmagic[i]; + if (write(fd, map->magic[i], len) != (ssize_t)len) { + file_error(ms, errno, "error writing `%s'", dbname); + goto out; + } + } + + if (fd != -1) + (void)close(fd); + rv = 0; +out: + free(dbname); + return rv; +} + +private const char ext[] = ".mgc"; +/* + * make a dbname + */ +private char * +mkdbname(struct magic_set *ms, const char *fn, int strip) +{ + const char *p, *q; + char *buf; + + if (strip) { + if ((p = strrchr(fn, '/')) != NULL) + fn = ++p; + } + + for (q = fn; *q; q++) + continue; + /* Look for .mgc */ + for (p = ext + sizeof(ext) - 1; p >= ext && q >= fn; p--, q--) + if (*p != *q) + break; + + /* Did not find .mgc, restore q */ + if (p >= ext) + while (*q) + q++; + + q++; + /* Compatibility with old code that looked in .mime */ + if (ms->flags & MAGIC_MIME) { + if (asprintf(&buf, "%.*s.mime%s", (int)(q - fn), fn, ext) < 0) + return NULL; + if (access(buf, R_OK) != -1) { + ms->flags &= MAGIC_MIME_TYPE; + return buf; + } + free(buf); + } + if (asprintf(&buf, "%.*s%s", (int)(q - fn), fn, ext) < 0) + return NULL; + + /* Compatibility with old code that looked in .mime */ + if (strstr(p, ".mime") != NULL) + ms->flags &= MAGIC_MIME_TYPE; + return buf; +} + +/* + * Byteswap an mmap'ed file if needed + */ +private void +byteswap(struct magic *magic, uint32_t nmagic) +{ + uint32_t i; + for (i = 0; i < nmagic; i++) + bs1(&magic[i]); +} + +/* + * swap a short + */ +private uint16_t +swap2(uint16_t sv) +{ + uint16_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[1]; + d[1] = s[0]; + return rv; +} + +/* + * swap an int + */ +private uint32_t +swap4(uint32_t sv) +{ + uint32_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + return rv; +} + +/* + * swap a quad + */ +private uint64_t +swap8(uint64_t sv) +{ + uint64_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; +#if 0 + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + d[4] = s[7]; + d[5] = s[6]; + d[6] = s[5]; + d[7] = s[4]; +#else + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; +#endif + return rv; +} + +/* + * byteswap a single magic entry + */ +private void +bs1(struct magic *m) +{ + m->cont_level = swap2(m->cont_level); + m->offset = swap4((uint32_t)m->offset); + m->in_offset = swap4((uint32_t)m->in_offset); + m->lineno = swap4((uint32_t)m->lineno); + if (IS_STRING(m->type)) { + m->str_range = swap4(m->str_range); + m->str_flags = swap4(m->str_flags); + } + else { + m->value.q = swap8(m->value.q); + m->num_mask = swap8(m->num_mask); + } +} + +protected size_t +file_pstring_length_size(const struct magic *m) +{ + switch (m->str_flags & PSTRING_LEN) { + case PSTRING_1_LE: + return 1; + case PSTRING_2_LE: + case PSTRING_2_BE: + return 2; + case PSTRING_4_LE: + case PSTRING_4_BE: + return 4; + default: + abort(); /* Impossible */ + return 1; + } +} +protected size_t +file_pstring_get_length(const struct magic *m, const char *s) +{ + size_t len = 0; + + switch (m->str_flags & PSTRING_LEN) { + case PSTRING_1_LE: + len = *s; + break; + case PSTRING_2_LE: + len = (s[1] << 8) | s[0]; + break; + case PSTRING_2_BE: + len = (s[0] << 8) | s[1]; + break; + case PSTRING_4_LE: + len = (s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0]; + break; + case PSTRING_4_BE: + len = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; + break; + default: + abort(); /* Impossible */ + } + + if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF) + len -= file_pstring_length_size(m); + + return len; +} + +protected int +file_magicfind(struct magic_set *ms, const char *name, struct mlist *v) +{ + uint32_t i, j; + struct mlist *mlist, *ml; + + mlist = ms->mlist[1]; + + for (ml = mlist->next; ml != mlist; ml = ml->next) { + struct magic *ma = ml->magic; + uint32_t nma = ml->nmagic; + for (i = 0; i < nma; i++) { + if (ma[i].type != FILE_NAME) + continue; + if (strcmp(ma[i].value.s, name) == 0) { + v->magic = &ma[i]; + for (j = i + 1; j < nma; j++) + if (ma[j].cont_level == 0) + break; + v->nmagic = j - i; + return 0; + } + } + } + return -1; +} diff --git a/contrib/file/src/apptype.c b/contrib/file/src/apptype.c new file mode 100644 index 0000000..fcd764e --- /dev/null +++ b/contrib/file/src/apptype.c @@ -0,0 +1,169 @@ +/* + * Adapted from: apptype.c, Written by Eberhard Mattes and put into the + * public domain + * + * Notes: 1. Qualify the filename so that DosQueryAppType does not do extraneous + * searches. + * + * 2. DosQueryAppType will return FAPPTYP_DOS on a file ending with ".com" + * (other than an OS/2 exe or Win exe with this name). Eberhard Mattes + * remarks Tue, 6 Apr 93: Moreover, it reports the type of the (new and very + * bug ridden) Win Emacs as "OS/2 executable". + * + * 3. apptype() uses the filename if given, otherwise a tmp file is created with + * the contents of buf. If buf is not the complete file, apptype can + * incorrectly identify the exe type. The "-z" option of "file" is the reason + * for this ugly code. + */ + +/* + * amai: Darrel Hankerson did the changes described here. + * + * It remains to check the validity of comments (2.) since it's referred to an + * "old" OS/2 version. + * + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: apptype.c,v 1.13 2011/09/07 21:57:15 christos Exp $") +#endif /* lint */ + +#include <stdlib.h> +#include <string.h> + +#ifdef __EMX__ +#include <io.h> +#define INCL_DOSSESMGR +#define INCL_DOSERRORS +#define INCL_DOSFILEMGR +#include <os2.h> +typedef ULONG APPTYPE; + +protected int +file_os2_apptype(struct magic_set *ms, const char *fn, const void *buf, + size_t nb) +{ + APPTYPE rc, type; + char path[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR], + fname[_MAX_FNAME], ext[_MAX_EXT]; + char *filename; + FILE *fp; + + if (fn) + filename = strdup(fn); + else if ((filename = tempnam("./", "tmp")) == NULL) { + file_error(ms, errno, "cannot create tempnam"); + return -1; + } + /* qualify the filename to prevent extraneous searches */ + _splitpath(filename, drive, dir, fname, ext); + (void)sprintf(path, "%s%s%s%s", drive, + (*dir == '\0') ? "./" : dir, + fname, + (*ext == '\0') ? "." : ext); + + if (fn == NULL) { + if ((fp = fopen(path, "wb")) == NULL) { + file_error(ms, errno, "cannot open tmp file `%s'", path); + return -1; + } + if (fwrite(buf, 1, nb, fp) != nb) { + file_error(ms, errno, "cannot write tmp file `%s'", + path); + (void)fclose(fp); + return -1; + } + (void)fclose(fp); + } + rc = DosQueryAppType((unsigned char *)path, &type); + + if (fn == NULL) { + unlink(path); + free(filename); + } +#if 0 + if (rc == ERROR_INVALID_EXE_SIGNATURE) + printf("%s: not an executable file\n", fname); + else if (rc == ERROR_FILE_NOT_FOUND) + printf("%s: not found\n", fname); + else if (rc == ERROR_ACCESS_DENIED) + printf("%s: access denied\n", fname); + else if (rc != 0) + printf("%s: error code = %lu\n", fname, rc); + else +#else + + /* + * for our purpose here it's sufficient to just ignore the error and + * return w/o success (=0) + */ + + if (rc) + return (0); + +#endif + + if (type & FAPPTYP_32BIT) + if (file_printf(ms, "32-bit ") == -1) + return -1; + if (type & FAPPTYP_PHYSDRV) { + if (file_printf(ms, "physical device driver") == -1) + return -1; + } else if (type & FAPPTYP_VIRTDRV) { + if (file_printf(ms, "virtual device driver") == -1) + return -1; + } else if (type & FAPPTYP_DLL) { + if (type & FAPPTYP_PROTDLL) + if (file_printf(ms, "protected ") == -1) + return -1; + if (file_printf(ms, "DLL") == -1) + return -1; + } else if (type & (FAPPTYP_WINDOWSREAL | FAPPTYP_WINDOWSPROT)) { + if (file_printf(ms, "Windows executable") == -1) + return -1; + } else if (type & FAPPTYP_DOS) { + /* + * The API routine is partially broken on filenames ending + * ".com". + */ + if (stricmp(ext, ".com") == 0) + if (strncmp((const char *)buf, "MZ", 2)) + return (0); + if (file_printf(ms, "DOS executable") == -1) + return -1; + /* ---------------------------------------- */ + /* Might learn more from the magic(4) entry */ + if (file_printf(ms, ", magic(4)-> ") == -1) + return -1; + return (0); + /* ---------------------------------------- */ + } else if (type & FAPPTYP_BOUND) { + if (file_printf(ms, "bound executable") == -1) + return -1; + } else if ((type & 7) == FAPPTYP_WINDOWAPI) { + if (file_printf(ms, "PM executable") == -1) + return -1; + } else if (file_printf(ms, "OS/2 executable") == -1) + return -1; + + switch (type & (FAPPTYP_NOTWINDOWCOMPAT | + FAPPTYP_WINDOWCOMPAT | + FAPPTYP_WINDOWAPI)) { + case FAPPTYP_NOTWINDOWCOMPAT: + if (file_printf(ms, " [NOTWINDOWCOMPAT]") == -1) + return -1; + break; + case FAPPTYP_WINDOWCOMPAT: + if (file_printf(ms, " [WINDOWCOMPAT]") == -1) + return -1; + break; + case FAPPTYP_WINDOWAPI: + if (file_printf(ms, " [WINDOWAPI]") == -1) + return -1; + break; + } + return 1; +} +#endif diff --git a/contrib/file/src/ascmagic.c b/contrib/file/src/ascmagic.c new file mode 100644 index 0000000..ca26665 --- /dev/null +++ b/contrib/file/src/ascmagic.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * ASCII magic -- try to detect text encoding. + * + * Extensively modified by Eric Fischer <enf@pobox.com> in July, 2000, + * to handle character codes other than ASCII on a unified basis. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: ascmagic.c,v 1.88 2014/02/12 23:20:53 christos Exp $") +#endif /* lint */ + +#include "magic.h" +#include <string.h> +#include <memory.h> +#include <ctype.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#define MAXLINELEN 300 /* longest sane line length */ +#define ISSPC(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n' \ + || (x) == 0x85 || (x) == '\f') + +private unsigned char *encode_utf8(unsigned char *, size_t, unichar *, size_t); +private size_t trim_nuls(const unsigned char *, size_t); + +/* + * Undo the NUL-termination kindly provided by process() + * but leave at least one byte to look at + */ +private size_t +trim_nuls(const unsigned char *buf, size_t nbytes) +{ + while (nbytes > 1 && buf[nbytes - 1] == '\0') + nbytes--; + + return nbytes; +} + +protected int +file_ascmagic(struct magic_set *ms, const unsigned char *buf, size_t nbytes, + int text) +{ + unichar *ubuf = NULL; + size_t ulen = 0; + int rv = 1; + + const char *code = NULL; + const char *code_mime = NULL; + const char *type = NULL; + + if (ms->flags & MAGIC_APPLE) + return 0; + + nbytes = trim_nuls(buf, nbytes); + + /* If file doesn't look like any sort of text, give up. */ + if (file_encoding(ms, buf, nbytes, &ubuf, &ulen, &code, &code_mime, + &type) == 0) + rv = 0; + else + rv = file_ascmagic_with_encoding(ms, buf, nbytes, ubuf, ulen, code, + type, text); + + free(ubuf); + + return rv; +} + +protected int +file_ascmagic_with_encoding(struct magic_set *ms, const unsigned char *buf, + size_t nbytes, unichar *ubuf, size_t ulen, const char *code, + const char *type, int text) +{ + unsigned char *utf8_buf = NULL, *utf8_end; + size_t mlen, i; + int rv = -1; + int mime = ms->flags & MAGIC_MIME; + + const char *subtype = NULL; + const char *subtype_mime = NULL; + + int has_escapes = 0; + int has_backspace = 0; + int seen_cr = 0; + + int n_crlf = 0; + int n_lf = 0; + int n_cr = 0; + int n_nel = 0; + int executable = 0; + + size_t last_line_end = (size_t)-1; + int has_long_lines = 0; + + if (ms->flags & MAGIC_APPLE) + return 0; + + nbytes = trim_nuls(buf, nbytes); + + /* If we have fewer than 2 bytes, give up. */ + if (nbytes <= 1) { + rv = 0; + goto done; + } + + if (ulen > 0 && (ms->flags & MAGIC_NO_CHECK_SOFT) == 0) { + /* Convert ubuf to UTF-8 and try text soft magic */ + /* malloc size is a conservative overestimate; could be + improved, or at least realloced after conversion. */ + mlen = ulen * 6; + if ((utf8_buf = CAST(unsigned char *, malloc(mlen))) == NULL) { + file_oomem(ms, mlen); + goto done; + } + if ((utf8_end = encode_utf8(utf8_buf, mlen, ubuf, ulen)) + == NULL) + goto done; + if ((rv = file_softmagic(ms, utf8_buf, + (size_t)(utf8_end - utf8_buf), 0, TEXTTEST, text)) == 0) + rv = -1; + } + + /* Now try to discover other details about the file. */ + for (i = 0; i < ulen; i++) { + if (ubuf[i] == '\n') { + if (seen_cr) + n_crlf++; + else + n_lf++; + last_line_end = i; + } else if (seen_cr) + n_cr++; + + seen_cr = (ubuf[i] == '\r'); + if (seen_cr) + last_line_end = i; + + if (ubuf[i] == 0x85) { /* X3.64/ECMA-43 "next line" character */ + n_nel++; + last_line_end = i; + } + + /* If this line is _longer_ than MAXLINELEN, remember it. */ + if (i > last_line_end + MAXLINELEN) + has_long_lines = 1; + + if (ubuf[i] == '\033') + has_escapes = 1; + if (ubuf[i] == '\b') + has_backspace = 1; + } + + /* Beware, if the data has been truncated, the final CR could have + been followed by a LF. If we have HOWMANY bytes, it indicates + that the data might have been truncated, probably even before + this function was called. */ + if (seen_cr && nbytes < HOWMANY) + n_cr++; + + if (strcmp(type, "binary") == 0) { + rv = 0; + goto done; + } + if (mime) { + if (!file_printedlen(ms) && (mime & MAGIC_MIME_TYPE) != 0) { + if (subtype_mime) { + if (file_printf(ms, "%s", subtype_mime) == -1) + goto done; + } else { + if (file_printf(ms, "text/plain") == -1) + goto done; + } + } + } else { + if (file_printedlen(ms)) { + switch (file_replace(ms, " text$", ", ")) { + case 0: + switch (file_replace(ms, " text executable$", + ", ")) { + case 0: + if (file_printf(ms, ", ") == -1) + goto done; + break; + case -1: + goto done; + default: + executable = 1; + break; + } + break; + case -1: + goto done; + default: + break; + } + } + + if (file_printf(ms, "%s", code) == -1) + goto done; + + if (subtype) { + if (file_printf(ms, " %s", subtype) == -1) + goto done; + } + + if (file_printf(ms, " %s", type) == -1) + goto done; + + if (executable) + if (file_printf(ms, " executable") == -1) + goto done; + + if (has_long_lines) + if (file_printf(ms, ", with very long lines") == -1) + goto done; + + /* + * Only report line terminators if we find one other than LF, + * or if we find none at all. + */ + if ((n_crlf == 0 && n_cr == 0 && n_nel == 0 && n_lf == 0) || + (n_crlf != 0 || n_cr != 0 || n_nel != 0)) { + if (file_printf(ms, ", with") == -1) + goto done; + + if (n_crlf == 0 && n_cr == 0 && n_nel == 0 && n_lf == 0) { + if (file_printf(ms, " no") == -1) + goto done; + } else { + if (n_crlf) { + if (file_printf(ms, " CRLF") == -1) + goto done; + if (n_cr || n_lf || n_nel) + if (file_printf(ms, ",") == -1) + goto done; + } + if (n_cr) { + if (file_printf(ms, " CR") == -1) + goto done; + if (n_lf || n_nel) + if (file_printf(ms, ",") == -1) + goto done; + } + if (n_lf) { + if (file_printf(ms, " LF") == -1) + goto done; + if (n_nel) + if (file_printf(ms, ",") == -1) + goto done; + } + if (n_nel) + if (file_printf(ms, " NEL") == -1) + goto done; + } + + if (file_printf(ms, " line terminators") == -1) + goto done; + } + + if (has_escapes) + if (file_printf(ms, ", with escape sequences") == -1) + goto done; + if (has_backspace) + if (file_printf(ms, ", with overstriking") == -1) + goto done; + } + rv = 1; +done: + free(utf8_buf); + + return rv; +} + +/* + * Encode Unicode string as UTF-8, returning pointer to character + * after end of string, or NULL if an invalid character is found. + */ +private unsigned char * +encode_utf8(unsigned char *buf, size_t len, unichar *ubuf, size_t ulen) +{ + size_t i; + unsigned char *end = buf + len; + + for (i = 0; i < ulen; i++) { + if (ubuf[i] <= 0x7f) { + if (end - buf < 1) + return NULL; + *buf++ = (unsigned char)ubuf[i]; + } else if (ubuf[i] <= 0x7ff) { + if (end - buf < 2) + return NULL; + *buf++ = (unsigned char)((ubuf[i] >> 6) + 0xc0); + *buf++ = (unsigned char)((ubuf[i] & 0x3f) + 0x80); + } else if (ubuf[i] <= 0xffff) { + if (end - buf < 3) + return NULL; + *buf++ = (unsigned char)((ubuf[i] >> 12) + 0xe0); + *buf++ = (unsigned char)(((ubuf[i] >> 6) & 0x3f) + 0x80); + *buf++ = (unsigned char)((ubuf[i] & 0x3f) + 0x80); + } else if (ubuf[i] <= 0x1fffff) { + if (end - buf < 4) + return NULL; + *buf++ = (unsigned char)((ubuf[i] >> 18) + 0xf0); + *buf++ = (unsigned char)(((ubuf[i] >> 12) & 0x3f) + 0x80); + *buf++ = (unsigned char)(((ubuf[i] >> 6) & 0x3f) + 0x80); + *buf++ = (unsigned char)((ubuf[i] & 0x3f) + 0x80); + } else if (ubuf[i] <= 0x3ffffff) { + if (end - buf < 5) + return NULL; + *buf++ = (unsigned char)((ubuf[i] >> 24) + 0xf8); + *buf++ = (unsigned char)(((ubuf[i] >> 18) & 0x3f) + 0x80); + *buf++ = (unsigned char)(((ubuf[i] >> 12) & 0x3f) + 0x80); + *buf++ = (unsigned char)(((ubuf[i] >> 6) & 0x3f) + 0x80); + *buf++ = (unsigned char)((ubuf[i] & 0x3f) + 0x80); + } else if (ubuf[i] <= 0x7fffffff) { + if (end - buf < 6) + return NULL; + *buf++ = (unsigned char)((ubuf[i] >> 30) + 0xfc); + *buf++ = (unsigned char)(((ubuf[i] >> 24) & 0x3f) + 0x80); + *buf++ = (unsigned char)(((ubuf[i] >> 18) & 0x3f) + 0x80); + *buf++ = (unsigned char)(((ubuf[i] >> 12) & 0x3f) + 0x80); + *buf++ = (unsigned char)(((ubuf[i] >> 6) & 0x3f) + 0x80); + *buf++ = (unsigned char)((ubuf[i] & 0x3f) + 0x80); + } else /* Invalid character */ + return NULL; + } + + return buf; +} diff --git a/contrib/file/src/asctime_r.c b/contrib/file/src/asctime_r.c new file mode 100644 index 0000000..3a0ec044 --- /dev/null +++ b/contrib/file/src/asctime_r.c @@ -0,0 +1,19 @@ +/* $File: asctime_r.c,v 1.1 2012/05/15 17:14:36 christos Exp $ */ + +#include "file.h" +#ifndef lint +FILE_RCSID("@(#)$File: asctime_r.c,v 1.1 2012/05/15 17:14:36 christos Exp $") +#endif /* lint */ +#include <time.h> +#include <string.h> + +/* asctime_r is not thread-safe anyway */ +char * +asctime_r(const struct tm *t, char *dst) +{ + char *p = asctime(t); + if (p == NULL) + return NULL; + memcpy(dst, p, 26); + return dst; +} diff --git a/contrib/file/src/asprintf.c b/contrib/file/src/asprintf.c new file mode 100644 index 0000000..30852d2 --- /dev/null +++ b/contrib/file/src/asprintf.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: asprintf.c,v 1.4 2010/07/21 16:47:17 christos Exp $") +#endif + +int asprintf(char **ptr, const char *fmt, ...) +{ + va_list vargs; + int retval; + + va_start(vargs, fmt); + retval = vasprintf(ptr, fmt, vargs); + va_end(vargs); + + return retval; +} diff --git a/contrib/file/src/cdf.c b/contrib/file/src/cdf.c new file mode 100644 index 0000000..106fc7a --- /dev/null +++ b/contrib/file/src/cdf.c @@ -0,0 +1,1368 @@ +/*- + * Copyright (c) 2008 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Parse Composite Document Files, the format used in Microsoft Office + * document files before they switched to zipped XML. + * Info from: http://sc.openoffice.org/compdocfileformat.pdf + * + * N.B. This is the "Composite Document File" format, and not the + * "Compound Document Format", nor the "Channel Definition Format". + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: cdf.c,v 1.63 2014/06/09 13:04:37 christos Exp $") +#endif + +#include <assert.h> +#ifdef CDF_DEBUG +#include <err.h> +#endif +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#ifndef EFTYPE +#define EFTYPE EINVAL +#endif + +#include "cdf.h" + +#ifdef CDF_DEBUG +#define DPRINTF(a) printf a, fflush(stdout) +#else +#define DPRINTF(a) +#endif + +static union { + char s[4]; + uint32_t u; +} cdf_bo; + +#define NEED_SWAP (cdf_bo.u == (uint32_t)0x01020304) + +#define CDF_TOLE8(x) ((uint64_t)(NEED_SWAP ? _cdf_tole8(x) : (uint64_t)(x))) +#define CDF_TOLE4(x) ((uint32_t)(NEED_SWAP ? _cdf_tole4(x) : (uint32_t)(x))) +#define CDF_TOLE2(x) ((uint16_t)(NEED_SWAP ? _cdf_tole2(x) : (uint16_t)(x))) +#define CDF_GETUINT32(x, y) cdf_getuint32(x, y) + + +/* + * swap a short + */ +static uint16_t +_cdf_tole2(uint16_t sv) +{ + uint16_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[1]; + d[1] = s[0]; + return rv; +} + +/* + * swap an int + */ +static uint32_t +_cdf_tole4(uint32_t sv) +{ + uint32_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + return rv; +} + +/* + * swap a quad + */ +static uint64_t +_cdf_tole8(uint64_t sv) +{ + uint64_t rv; + uint8_t *s = (uint8_t *)(void *)&sv; + uint8_t *d = (uint8_t *)(void *)&rv; + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; + return rv; +} + +/* + * grab a uint32_t from a possibly unaligned address, and return it in + * the native host order. + */ +static uint32_t +cdf_getuint32(const uint8_t *p, size_t offs) +{ + uint32_t rv; + (void)memcpy(&rv, p + offs * sizeof(uint32_t), sizeof(rv)); + return CDF_TOLE4(rv); +} + +#define CDF_UNPACK(a) \ + (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a) +#define CDF_UNPACKA(a) \ + (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a) + +uint16_t +cdf_tole2(uint16_t sv) +{ + return CDF_TOLE2(sv); +} + +uint32_t +cdf_tole4(uint32_t sv) +{ + return CDF_TOLE4(sv); +} + +uint64_t +cdf_tole8(uint64_t sv) +{ + return CDF_TOLE8(sv); +} + +void +cdf_swap_header(cdf_header_t *h) +{ + size_t i; + + h->h_magic = CDF_TOLE8(h->h_magic); + h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]); + h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]); + h->h_revision = CDF_TOLE2(h->h_revision); + h->h_version = CDF_TOLE2(h->h_version); + h->h_byte_order = CDF_TOLE2(h->h_byte_order); + h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2); + h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2); + h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat); + h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory); + h->h_min_size_standard_stream = + CDF_TOLE4(h->h_min_size_standard_stream); + h->h_secid_first_sector_in_short_sat = + CDF_TOLE4((uint32_t)h->h_secid_first_sector_in_short_sat); + h->h_num_sectors_in_short_sat = + CDF_TOLE4(h->h_num_sectors_in_short_sat); + h->h_secid_first_sector_in_master_sat = + CDF_TOLE4((uint32_t)h->h_secid_first_sector_in_master_sat); + h->h_num_sectors_in_master_sat = + CDF_TOLE4(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + h->h_master_sat[i] = CDF_TOLE4((uint32_t)h->h_master_sat[i]); +} + +void +cdf_unpack_header(cdf_header_t *h, char *buf) +{ + size_t i; + size_t len = 0; + + CDF_UNPACK(h->h_magic); + CDF_UNPACKA(h->h_uuid); + CDF_UNPACK(h->h_revision); + CDF_UNPACK(h->h_version); + CDF_UNPACK(h->h_byte_order); + CDF_UNPACK(h->h_sec_size_p2); + CDF_UNPACK(h->h_short_sec_size_p2); + CDF_UNPACKA(h->h_unused0); + CDF_UNPACK(h->h_num_sectors_in_sat); + CDF_UNPACK(h->h_secid_first_directory); + CDF_UNPACKA(h->h_unused1); + CDF_UNPACK(h->h_min_size_standard_stream); + CDF_UNPACK(h->h_secid_first_sector_in_short_sat); + CDF_UNPACK(h->h_num_sectors_in_short_sat); + CDF_UNPACK(h->h_secid_first_sector_in_master_sat); + CDF_UNPACK(h->h_num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) + CDF_UNPACK(h->h_master_sat[i]); +} + +void +cdf_swap_dir(cdf_directory_t *d) +{ + d->d_namelen = CDF_TOLE2(d->d_namelen); + d->d_left_child = CDF_TOLE4((uint32_t)d->d_left_child); + d->d_right_child = CDF_TOLE4((uint32_t)d->d_right_child); + d->d_storage = CDF_TOLE4((uint32_t)d->d_storage); + d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]); + d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]); + d->d_flags = CDF_TOLE4(d->d_flags); + d->d_created = CDF_TOLE8((uint64_t)d->d_created); + d->d_modified = CDF_TOLE8((uint64_t)d->d_modified); + d->d_stream_first_sector = CDF_TOLE4((uint32_t)d->d_stream_first_sector); + d->d_size = CDF_TOLE4(d->d_size); +} + +void +cdf_swap_class(cdf_classid_t *d) +{ + d->cl_dword = CDF_TOLE4(d->cl_dword); + d->cl_word[0] = CDF_TOLE2(d->cl_word[0]); + d->cl_word[1] = CDF_TOLE2(d->cl_word[1]); +} + +void +cdf_unpack_dir(cdf_directory_t *d, char *buf) +{ + size_t len = 0; + + CDF_UNPACKA(d->d_name); + CDF_UNPACK(d->d_namelen); + CDF_UNPACK(d->d_type); + CDF_UNPACK(d->d_color); + CDF_UNPACK(d->d_left_child); + CDF_UNPACK(d->d_right_child); + CDF_UNPACK(d->d_storage); + CDF_UNPACKA(d->d_storage_uuid); + CDF_UNPACK(d->d_flags); + CDF_UNPACK(d->d_created); + CDF_UNPACK(d->d_modified); + CDF_UNPACK(d->d_stream_first_sector); + CDF_UNPACK(d->d_size); + CDF_UNPACK(d->d_unused0); +} + +static int +cdf_check_stream_offset(const cdf_stream_t *sst, const cdf_header_t *h, + const void *p, size_t tail, int line) +{ + const char *b = (const char *)sst->sst_tab; + const char *e = ((const char *)p) + tail; + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + (void)&line; + if (e >= b && (size_t)(e - b) <= ss * sst->sst_len) + return 0; + DPRINTF(("%d: offset begin %p < end %p || %" SIZE_T_FORMAT "u" + " > %" SIZE_T_FORMAT "u [%" SIZE_T_FORMAT "u %" + SIZE_T_FORMAT "u]\n", line, b, e, (size_t)(e - b), + ss * sst->sst_len, ss, sst->sst_len)); + errno = EFTYPE; + return -1; +} + +static ssize_t +cdf_read(const cdf_info_t *info, off_t off, void *buf, size_t len) +{ + size_t siz = (size_t)off + len; + + if ((off_t)(off + len) != (off_t)siz) { + errno = EINVAL; + return -1; + } + + if (info->i_buf != NULL && info->i_len >= siz) { + (void)memcpy(buf, &info->i_buf[off], len); + return (ssize_t)len; + } + + if (info->i_fd == -1) + return -1; + + if (pread(info->i_fd, buf, len, off) != (ssize_t)len) + return -1; + + return (ssize_t)len; +} + +int +cdf_read_header(const cdf_info_t *info, cdf_header_t *h) +{ + char buf[512]; + + (void)memcpy(cdf_bo.s, "\01\02\03\04", 4); + if (cdf_read(info, (off_t)0, buf, sizeof(buf)) == -1) + return -1; + cdf_unpack_header(h, buf); + cdf_swap_header(h); + if (h->h_magic != CDF_MAGIC) { + DPRINTF(("Bad magic 0x%" INT64_T_FORMAT "x != 0x%" + INT64_T_FORMAT "x\n", + (unsigned long long)h->h_magic, + (unsigned long long)CDF_MAGIC)); + goto out; + } + if (h->h_sec_size_p2 > 20) { + DPRINTF(("Bad sector size 0x%u\n", h->h_sec_size_p2)); + goto out; + } + if (h->h_short_sec_size_p2 > 20) { + DPRINTF(("Bad short sector size 0x%u\n", + h->h_short_sec_size_p2)); + goto out; + } + return 0; +out: + errno = EFTYPE; + return -1; +} + + +ssize_t +cdf_read_sector(const cdf_info_t *info, void *buf, size_t offs, size_t len, + const cdf_header_t *h, cdf_secid_t id) +{ + size_t ss = CDF_SEC_SIZE(h); + size_t pos = CDF_SEC_POS(h, id); + assert(ss == len); + return cdf_read(info, (off_t)pos, ((char *)buf) + offs, len); +} + +ssize_t +cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs, + size_t len, const cdf_header_t *h, cdf_secid_t id) +{ + size_t ss = CDF_SHORT_SEC_SIZE(h); + size_t pos = CDF_SHORT_SEC_POS(h, id); + assert(ss == len); + if (pos + len > CDF_SEC_SIZE(h) * sst->sst_len) { + DPRINTF(("Out of bounds read %" SIZE_T_FORMAT "u > %" + SIZE_T_FORMAT "u\n", + pos + len, CDF_SEC_SIZE(h) * sst->sst_len)); + return -1; + } + (void)memcpy(((char *)buf) + offs, + ((const char *)sst->sst_tab) + pos, len); + return len; +} + +/* + * Read the sector allocation table. + */ +int +cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat) +{ + size_t i, j, k; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t *msa, mid, sec; + size_t nsatpersec = (ss / sizeof(mid)) - 1; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + +#define CDF_SEC_LIMIT (UINT32_MAX / (4 * ss)) + if ((nsatpersec > 0 && + h->h_num_sectors_in_master_sat > CDF_SEC_LIMIT / nsatpersec) || + i > CDF_SEC_LIMIT) { + DPRINTF(("Number of sectors in master SAT too big %u %" + SIZE_T_FORMAT "u\n", h->h_num_sectors_in_master_sat, i)); + errno = EFTYPE; + return -1; + } + + sat->sat_len = h->h_num_sectors_in_master_sat * nsatpersec + i; + DPRINTF(("sat_len = %" SIZE_T_FORMAT "u ss = %" SIZE_T_FORMAT "u\n", + sat->sat_len, ss)); + if ((sat->sat_tab = CAST(cdf_secid_t *, calloc(sat->sat_len, ss))) + == NULL) + return -1; + + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] < 0) + break; + if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h, + h->h_master_sat[i]) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", h->h_master_sat[i])); + goto out1; + } + } + + if ((msa = CAST(cdf_secid_t *, calloc(1, ss))) == NULL) + goto out1; + + mid = h->h_secid_first_sector_in_master_sat; + for (j = 0; j < h->h_num_sectors_in_master_sat; j++) { + if (mid < 0) + goto out; + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Reading master sector loop limit")); + errno = EFTYPE; + goto out2; + } + if (cdf_read_sector(info, msa, 0, ss, h, mid) != (ssize_t)ss) { + DPRINTF(("Reading master sector %d", mid)); + goto out2; + } + for (k = 0; k < nsatpersec; k++, i++) { + sec = CDF_TOLE4((uint32_t)msa[k]); + if (sec < 0) + goto out; + if (i >= sat->sat_len) { + DPRINTF(("Out of bounds reading MSA %" SIZE_T_FORMAT + "u >= %" SIZE_T_FORMAT "u", i, sat->sat_len)); + errno = EFTYPE; + goto out2; + } + if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h, + sec) != (ssize_t)ss) { + DPRINTF(("Reading sector %d", + CDF_TOLE4(msa[k]))); + goto out2; + } + } + mid = CDF_TOLE4((uint32_t)msa[nsatpersec]); + } +out: + sat->sat_len = i; + free(msa); + return 0; +out2: + free(msa); +out1: + free(sat->sat_tab); + return -1; +} + +size_t +cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size) +{ + size_t i, j; + cdf_secid_t maxsector = (cdf_secid_t)((sat->sat_len * size) + / sizeof(maxsector)); + + DPRINTF(("Chain:")); + for (j = i = 0; sid >= 0; i++, j++) { + DPRINTF((" %d", sid)); + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Counting chain loop limit")); + errno = EFTYPE; + return (size_t)-1; + } + if (sid >= maxsector) { + DPRINTF(("Sector %d >= %d\n", sid, maxsector)); + errno = EFTYPE; + return (size_t)-1; + } + sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]); + } + if (i == 0) { + DPRINTF((" none, sid: %d\n", sid)); + return (size_t)-1; + + } + DPRINTF(("\n")); + return i; +} + +int +cdf_read_long_sector_chain(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SEC_SIZE(h), i, j; + ssize_t nr; + scn->sst_len = cdf_count_chain(sat, sid, ss); + scn->sst_dirlen = len; + + if (scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read long sector chain loop limit")); + errno = EFTYPE; + goto out; + } + if (i >= scn->sst_len) { + DPRINTF(("Out of bounds reading long sector chain " + "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i, + scn->sst_len)); + errno = EFTYPE; + goto out; + } + if ((nr = cdf_read_sector(info, scn->sst_tab, i * ss, ss, h, + sid)) != (ssize_t)ss) { + if (i == scn->sst_len - 1 && nr > 0) { + /* Last sector might be truncated */ + return 0; + } + DPRINTF(("Reading long sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]); + } + return 0; +out: + free(scn->sst_tab); + return -1; +} + +int +cdf_read_short_sector_chain(const cdf_header_t *h, + const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + size_t ss = CDF_SHORT_SEC_SIZE(h), i, j; + scn->sst_len = cdf_count_chain(ssat, sid, CDF_SEC_SIZE(h)); + scn->sst_dirlen = len; + + if (sst->sst_tab == NULL || scn->sst_len == (size_t)-1) + return -1; + + scn->sst_tab = calloc(scn->sst_len, ss); + if (scn->sst_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sector chain loop limit")); + errno = EFTYPE; + goto out; + } + if (i >= scn->sst_len) { + DPRINTF(("Out of bounds reading short sector chain " + "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", + i, scn->sst_len)); + errno = EFTYPE; + goto out; + } + if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h, + sid) != (ssize_t)ss) { + DPRINTF(("Reading short sector chain %d", sid)); + goto out; + } + sid = CDF_TOLE4((uint32_t)ssat->sat_tab[sid]); + } + return 0; +out: + free(scn->sst_tab); + return -1; +} + +int +cdf_read_sector_chain(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + cdf_secid_t sid, size_t len, cdf_stream_t *scn) +{ + + if (len < h->h_min_size_standard_stream && sst->sst_tab != NULL) + return cdf_read_short_sector_chain(h, ssat, sst, sid, len, + scn); + else + return cdf_read_long_sector_chain(info, h, sat, sid, len, scn); +} + +int +cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, cdf_dir_t *dir) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h), ns, nd; + char *buf; + cdf_secid_t sid = h->h_secid_first_directory; + + ns = cdf_count_chain(sat, sid, ss); + if (ns == (size_t)-1) + return -1; + + nd = ss / CDF_DIRECTORY_SIZE; + + dir->dir_len = ns * nd; + dir->dir_tab = CAST(cdf_directory_t *, + calloc(dir->dir_len, sizeof(dir->dir_tab[0]))); + if (dir->dir_tab == NULL) + return -1; + + if ((buf = CAST(char *, malloc(ss))) == NULL) { + free(dir->dir_tab); + return -1; + } + + for (j = i = 0; i < ns; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read dir loop limit")); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(info, buf, 0, ss, h, sid) != (ssize_t)ss) { + DPRINTF(("Reading directory sector %d", sid)); + goto out; + } + for (j = 0; j < nd; j++) { + cdf_unpack_dir(&dir->dir_tab[i * nd + j], + &buf[j * CDF_DIRECTORY_SIZE]); + } + sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]); + } + if (NEED_SWAP) + for (i = 0; i < dir->dir_len; i++) + cdf_swap_dir(&dir->dir_tab[i]); + free(buf); + return 0; +out: + free(dir->dir_tab); + free(buf); + return -1; +} + + +int +cdf_read_ssat(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, cdf_sat_t *ssat) +{ + size_t i, j; + size_t ss = CDF_SEC_SIZE(h); + cdf_secid_t sid = h->h_secid_first_sector_in_short_sat; + + ssat->sat_len = cdf_count_chain(sat, sid, CDF_SEC_SIZE(h)); + if (ssat->sat_len == (size_t)-1) + return -1; + + ssat->sat_tab = CAST(cdf_secid_t *, calloc(ssat->sat_len, ss)); + if (ssat->sat_tab == NULL) + return -1; + + for (j = i = 0; sid >= 0; i++, j++) { + if (j >= CDF_LOOP_LIMIT) { + DPRINTF(("Read short sat sector loop limit")); + errno = EFTYPE; + goto out; + } + if (i >= ssat->sat_len) { + DPRINTF(("Out of bounds reading short sector chain " + "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i, + ssat->sat_len)); + errno = EFTYPE; + goto out; + } + if (cdf_read_sector(info, ssat->sat_tab, i * ss, ss, h, sid) != + (ssize_t)ss) { + DPRINTF(("Reading short sat sector %d", sid)); + goto out; + } + sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]); + } + return 0; +out: + free(ssat->sat_tab); + return -1; +} + +int +cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_dir_t *dir, cdf_stream_t *scn, + const cdf_directory_t **root) +{ + size_t i; + const cdf_directory_t *d; + + *root = NULL; + for (i = 0; i < dir->dir_len; i++) + if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE) + break; + + /* If the it is not there, just fake it; some docs don't have it */ + if (i == dir->dir_len) + goto out; + d = &dir->dir_tab[i]; + *root = d; + + /* If the it is not there, just fake it; some docs don't have it */ + if (d->d_stream_first_sector < 0) + goto out; + + return cdf_read_long_sector_chain(info, h, sat, + d->d_stream_first_sector, d->d_size, scn); +out: + scn->sst_tab = NULL; + scn->sst_len = 0; + scn->sst_dirlen = 0; + return 0; +} + +static int +cdf_namecmp(const char *d, const uint16_t *s, size_t l) +{ + for (; l--; d++, s++) + if (*d != CDF_TOLE2(*s)) + return (unsigned char)*d - CDF_TOLE2(*s); + return 0; +} + +int +cdf_read_summary_info(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + return cdf_read_user_stream(info, h, sat, ssat, sst, dir, + "\05SummaryInformation", scn); +} + +int +cdf_read_user_stream(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir, const char *name, cdf_stream_t *scn) +{ + size_t i; + const cdf_directory_t *d; + size_t name_len = strlen(name) + 1; + + for (i = dir->dir_len; i > 0; i--) + if (dir->dir_tab[i - 1].d_type == CDF_DIR_TYPE_USER_STREAM && + cdf_namecmp(name, dir->dir_tab[i - 1].d_name, name_len) + == 0) + break; + + if (i == 0) { + DPRINTF(("Cannot find user stream `%s'\n", name)); + errno = ESRCH; + return -1; + } + d = &dir->dir_tab[i - 1]; + return cdf_read_sector_chain(info, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, scn); +} + +int +cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h, + uint32_t offs, cdf_property_info_t **info, size_t *count, size_t *maxcount) +{ + const cdf_section_header_t *shp; + cdf_section_header_t sh; + const uint8_t *p, *q, *e; + int16_t s16; + int32_t s32; + uint32_t u32; + int64_t s64; + uint64_t u64; + cdf_timestamp_t tp; + size_t i, o, o4, nelements, j; + cdf_property_info_t *inp; + + if (offs > UINT32_MAX / 4) { + errno = EFTYPE; + goto out; + } + shp = CAST(const cdf_section_header_t *, (const void *) + ((const char *)sst->sst_tab + offs)); + if (cdf_check_stream_offset(sst, h, shp, sizeof(*shp), __LINE__) == -1) + goto out; + sh.sh_len = CDF_TOLE4(shp->sh_len); +#define CDF_SHLEN_LIMIT (UINT32_MAX / 8) + if (sh.sh_len > CDF_SHLEN_LIMIT) { + errno = EFTYPE; + goto out; + } + sh.sh_properties = CDF_TOLE4(shp->sh_properties); +#define CDF_PROP_LIMIT (UINT32_MAX / (4 * sizeof(*inp))) + if (sh.sh_properties > CDF_PROP_LIMIT) + goto out; + DPRINTF(("section len: %u properties %u\n", sh.sh_len, + sh.sh_properties)); + if (*maxcount) { + if (*maxcount > CDF_PROP_LIMIT) + goto out; + *maxcount += sh.sh_properties; + inp = CAST(cdf_property_info_t *, + realloc(*info, *maxcount * sizeof(*inp))); + } else { + *maxcount = sh.sh_properties; + inp = CAST(cdf_property_info_t *, + malloc(*maxcount * sizeof(*inp))); + } + if (inp == NULL) + goto out; + *info = inp; + inp += *count; + *count += sh.sh_properties; + p = CAST(const uint8_t *, (const void *) + ((const char *)(const void *)sst->sst_tab + + offs + sizeof(sh))); + e = CAST(const uint8_t *, (const void *) + (((const char *)(const void *)shp) + sh.sh_len)); + if (cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1) + goto out; + for (i = 0; i < sh.sh_properties; i++) { + size_t tail = (i << 1) + 1; + if (cdf_check_stream_offset(sst, h, p, tail * sizeof(uint32_t), + __LINE__) == -1) + goto out; + size_t ofs = CDF_GETUINT32(p, tail); + q = (const uint8_t *)(const void *) + ((const char *)(const void *)p + ofs + - 2 * sizeof(uint32_t)); + if (q > e) { + DPRINTF(("Ran of the end %p > %p\n", q, e)); + goto out; + } + inp[i].pi_id = CDF_GETUINT32(p, i << 1); + inp[i].pi_type = CDF_GETUINT32(q, 0); + DPRINTF(("%" SIZE_T_FORMAT "u) id=%x type=%x offs=0x%tx,0x%x\n", + i, inp[i].pi_id, inp[i].pi_type, q - p, offs)); + if (inp[i].pi_type & CDF_VECTOR) { + nelements = CDF_GETUINT32(q, 1); + if (nelements == 0) { + DPRINTF(("CDF_VECTOR with nelements == 0\n")); + goto out; + } + o = 2; + } else { + nelements = 1; + o = 1; + } + o4 = o * sizeof(uint32_t); + if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED)) + goto unknown; + switch (inp[i].pi_type & CDF_TYPEMASK) { + case CDF_NULL: + case CDF_EMPTY: + break; + case CDF_SIGNED16: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s16, &q[o4], sizeof(s16)); + inp[i].pi_s16 = CDF_TOLE2(s16); + break; + case CDF_SIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s32, &q[o4], sizeof(s32)); + inp[i].pi_s32 = CDF_TOLE4((uint32_t)s32); + break; + case CDF_BOOL: + case CDF_UNSIGNED32: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u32, &q[o4], sizeof(u32)); + inp[i].pi_u32 = CDF_TOLE4(u32); + break; + case CDF_SIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&s64, &q[o4], sizeof(s64)); + inp[i].pi_s64 = CDF_TOLE8((uint64_t)s64); + break; + case CDF_UNSIGNED64: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u64, &q[o4], sizeof(u64)); + inp[i].pi_u64 = CDF_TOLE8((uint64_t)u64); + break; + case CDF_FLOAT: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u32, &q[o4], sizeof(u32)); + u32 = CDF_TOLE4(u32); + memcpy(&inp[i].pi_f, &u32, sizeof(inp[i].pi_f)); + break; + case CDF_DOUBLE: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&u64, &q[o4], sizeof(u64)); + u64 = CDF_TOLE8((uint64_t)u64); + memcpy(&inp[i].pi_d, &u64, sizeof(inp[i].pi_d)); + break; + case CDF_LENGTH32_STRING: + case CDF_LENGTH32_WSTRING: + if (nelements > 1) { + size_t nelem = inp - *info; + if (*maxcount > CDF_PROP_LIMIT + || nelements > CDF_PROP_LIMIT) + goto out; + *maxcount += nelements; + inp = CAST(cdf_property_info_t *, + realloc(*info, *maxcount * sizeof(*inp))); + if (inp == NULL) + goto out; + *info = inp; + inp = *info + nelem; + } + DPRINTF(("nelements = %" SIZE_T_FORMAT "u\n", + nelements)); + for (j = 0; j < nelements && i < sh.sh_properties; + j++, i++) + { + uint32_t l = CDF_GETUINT32(q, o); + inp[i].pi_str.s_len = l; + inp[i].pi_str.s_buf = (const char *) + (const void *)(&q[o4 + sizeof(l)]); + DPRINTF(("l = %d, r = %" SIZE_T_FORMAT + "u, s = %s\n", l, + CDF_ROUND(l, sizeof(l)), + inp[i].pi_str.s_buf)); + if (l & 1) + l++; + o += l >> 1; + if (q + o >= e) + goto out; + o4 = o * sizeof(uint32_t); + } + i--; + break; + case CDF_FILETIME: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + (void)memcpy(&tp, &q[o4], sizeof(tp)); + inp[i].pi_tp = CDF_TOLE8((uint64_t)tp); + break; + case CDF_CLIPBOARD: + if (inp[i].pi_type & CDF_VECTOR) + goto unknown; + break; + default: + unknown: + DPRINTF(("Don't know how to deal with %x\n", + inp[i].pi_type)); + break; + } + } + return 0; +out: + free(*info); + return -1; +} + +int +cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h, + cdf_summary_info_header_t *ssi, cdf_property_info_t **info, size_t *count) +{ + size_t maxcount; + const cdf_summary_info_header_t *si = + CAST(const cdf_summary_info_header_t *, sst->sst_tab); + const cdf_section_declaration_t *sd = + CAST(const cdf_section_declaration_t *, (const void *) + ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET)); + + if (cdf_check_stream_offset(sst, h, si, sizeof(*si), __LINE__) == -1 || + cdf_check_stream_offset(sst, h, sd, sizeof(*sd), __LINE__) == -1) + return -1; + ssi->si_byte_order = CDF_TOLE2(si->si_byte_order); + ssi->si_os_version = CDF_TOLE2(si->si_os_version); + ssi->si_os = CDF_TOLE2(si->si_os); + ssi->si_class = si->si_class; + cdf_swap_class(&ssi->si_class); + ssi->si_count = CDF_TOLE4(si->si_count); + *count = 0; + maxcount = 0; + *info = NULL; + if (cdf_read_property_info(sst, h, CDF_TOLE4(sd->sd_offset), info, + count, &maxcount) == -1) + return -1; + return 0; +} + + + +int +cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id) +{ + return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-" + "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0], + id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0], + id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4], + id->cl_six[5]); +} + +static const struct { + uint32_t v; + const char *n; +} vn[] = { + { CDF_PROPERTY_CODE_PAGE, "Code page" }, + { CDF_PROPERTY_TITLE, "Title" }, + { CDF_PROPERTY_SUBJECT, "Subject" }, + { CDF_PROPERTY_AUTHOR, "Author" }, + { CDF_PROPERTY_KEYWORDS, "Keywords" }, + { CDF_PROPERTY_COMMENTS, "Comments" }, + { CDF_PROPERTY_TEMPLATE, "Template" }, + { CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" }, + { CDF_PROPERTY_REVISION_NUMBER, "Revision Number" }, + { CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" }, + { CDF_PROPERTY_LAST_PRINTED, "Last Printed" }, + { CDF_PROPERTY_CREATE_TIME, "Create Time/Date" }, + { CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" }, + { CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" }, + { CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" }, + { CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" }, + { CDF_PROPERTY_THUMBNAIL, "Thumbnail" }, + { CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" }, + { CDF_PROPERTY_SECURITY, "Security" }, + { CDF_PROPERTY_LOCALE_ID, "Locale ID" }, +}; + +int +cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p) +{ + size_t i; + + for (i = 0; i < __arraycount(vn); i++) + if (vn[i].v == p) + return snprintf(buf, bufsiz, "%s", vn[i].n); + return snprintf(buf, bufsiz, "0x%x", p); +} + +int +cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts) +{ + int len = 0; + int days, hours, mins, secs; + + ts /= CDF_TIME_PREC; + secs = (int)(ts % 60); + ts /= 60; + mins = (int)(ts % 60); + ts /= 60; + hours = (int)(ts % 24); + ts /= 24; + days = (int)ts; + + if (days) { + len += snprintf(buf + len, bufsiz - len, "%dd+", days); + if ((size_t)len >= bufsiz) + return len; + } + + if (days || hours) { + len += snprintf(buf + len, bufsiz - len, "%.2d:", hours); + if ((size_t)len >= bufsiz) + return len; + } + + len += snprintf(buf + len, bufsiz - len, "%.2d:", mins); + if ((size_t)len >= bufsiz) + return len; + + len += snprintf(buf + len, bufsiz - len, "%.2d", secs); + return len; +} + + +#ifdef CDF_DEBUG +void +cdf_dump_header(const cdf_header_t *h) +{ + size_t i; + +#define DUMP(a, b) (void)fprintf(stderr, "%40.40s = " a "\n", # b, h->h_ ## b) +#define DUMP2(a, b) (void)fprintf(stderr, "%40.40s = " a " (" a ")\n", # b, \ + h->h_ ## b, 1 << h->h_ ## b) + DUMP("%d", revision); + DUMP("%d", version); + DUMP("0x%x", byte_order); + DUMP2("%d", sec_size_p2); + DUMP2("%d", short_sec_size_p2); + DUMP("%d", num_sectors_in_sat); + DUMP("%d", secid_first_directory); + DUMP("%d", min_size_standard_stream); + DUMP("%d", secid_first_sector_in_short_sat); + DUMP("%d", num_sectors_in_short_sat); + DUMP("%d", secid_first_sector_in_master_sat); + DUMP("%d", num_sectors_in_master_sat); + for (i = 0; i < __arraycount(h->h_master_sat); i++) { + if (h->h_master_sat[i] == CDF_SECID_FREE) + break; + (void)fprintf(stderr, "%35.35s[%.3zu] = %d\n", + "master_sat", i, h->h_master_sat[i]); + } +} + +void +cdf_dump_sat(const char *prefix, const cdf_sat_t *sat, size_t size) +{ + size_t i, j, s = size / sizeof(cdf_secid_t); + + for (i = 0; i < sat->sat_len; i++) { + (void)fprintf(stderr, "%s[%" SIZE_T_FORMAT "u]:\n%.6" + SIZE_T_FORMAT "u: ", prefix, i, i * s); + for (j = 0; j < s; j++) { + (void)fprintf(stderr, "%5d, ", + CDF_TOLE4(sat->sat_tab[s * i + j])); + if ((j + 1) % 10 == 0) + (void)fprintf(stderr, "\n%.6" SIZE_T_FORMAT + "u: ", i * s + j + 1); + } + (void)fprintf(stderr, "\n"); + } +} + +void +cdf_dump(void *v, size_t len) +{ + size_t i, j; + unsigned char *p = v; + char abuf[16]; + (void)fprintf(stderr, "%.4x: ", 0); + for (i = 0, j = 0; i < len; i++, p++) { + (void)fprintf(stderr, "%.2x ", *p); + abuf[j++] = isprint(*p) ? *p : '.'; + if (j == 16) { + j = 0; + abuf[15] = '\0'; + (void)fprintf(stderr, "%s\n%.4" SIZE_T_FORMAT "x: ", + abuf, i + 1); + } + } + (void)fprintf(stderr, "\n"); +} + +void +cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst) +{ + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + cdf_dump(sst->sst_tab, ss * sst->sst_len); +} + +void +cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir) +{ + size_t i, j; + cdf_directory_t *d; + char name[__arraycount(d->d_name)]; + cdf_stream_t scn; + struct timespec ts; + + static const char *types[] = { "empty", "user storage", + "user stream", "lockbytes", "property", "root storage" }; + + for (i = 0; i < dir->dir_len; i++) { + char buf[26]; + d = &dir->dir_tab[i]; + for (j = 0; j < sizeof(name); j++) + name[j] = (char)CDF_TOLE2(d->d_name[j]); + (void)fprintf(stderr, "Directory %" SIZE_T_FORMAT "u: %s\n", + i, name); + if (d->d_type < __arraycount(types)) + (void)fprintf(stderr, "Type: %s\n", types[d->d_type]); + else + (void)fprintf(stderr, "Type: %d\n", d->d_type); + (void)fprintf(stderr, "Color: %s\n", + d->d_color ? "black" : "red"); + (void)fprintf(stderr, "Left child: %d\n", d->d_left_child); + (void)fprintf(stderr, "Right child: %d\n", d->d_right_child); + (void)fprintf(stderr, "Flags: 0x%x\n", d->d_flags); + cdf_timestamp_to_timespec(&ts, d->d_created); + (void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec, buf)); + cdf_timestamp_to_timespec(&ts, d->d_modified); + (void)fprintf(stderr, "Modified %s", + cdf_ctime(&ts.tv_sec, buf)); + (void)fprintf(stderr, "Stream %d\n", d->d_stream_first_sector); + (void)fprintf(stderr, "Size %d\n", d->d_size); + switch (d->d_type) { + case CDF_DIR_TYPE_USER_STORAGE: + (void)fprintf(stderr, "Storage: %d\n", d->d_storage); + break; + case CDF_DIR_TYPE_USER_STREAM: + if (sst == NULL) + break; + if (cdf_read_sector_chain(info, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, &scn) == -1) { + warn("Can't read stream for %s at %d len %d", + name, d->d_stream_first_sector, d->d_size); + break; + } + cdf_dump_stream(h, &scn); + free(scn.sst_tab); + break; + default: + break; + } + + } +} + +void +cdf_dump_property_info(const cdf_property_info_t *info, size_t count) +{ + cdf_timestamp_t tp; + struct timespec ts; + char buf[64]; + size_t i, j; + + for (i = 0; i < count; i++) { + cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); + (void)fprintf(stderr, "%" SIZE_T_FORMAT "u) %s: ", i, buf); + switch (info[i].pi_type) { + case CDF_NULL: + break; + case CDF_SIGNED16: + (void)fprintf(stderr, "signed 16 [%hd]\n", + info[i].pi_s16); + break; + case CDF_SIGNED32: + (void)fprintf(stderr, "signed 32 [%d]\n", + info[i].pi_s32); + break; + case CDF_UNSIGNED32: + (void)fprintf(stderr, "unsigned 32 [%u]\n", + info[i].pi_u32); + break; + case CDF_FLOAT: + (void)fprintf(stderr, "float [%g]\n", + info[i].pi_f); + break; + case CDF_DOUBLE: + (void)fprintf(stderr, "double [%g]\n", + info[i].pi_d); + break; + case CDF_LENGTH32_STRING: + (void)fprintf(stderr, "string %u [%.*s]\n", + info[i].pi_str.s_len, + info[i].pi_str.s_len, info[i].pi_str.s_buf); + break; + case CDF_LENGTH32_WSTRING: + (void)fprintf(stderr, "string %u [", + info[i].pi_str.s_len); + for (j = 0; j < info[i].pi_str.s_len - 1; j++) + (void)fputc(info[i].pi_str.s_buf[j << 1], stderr); + (void)fprintf(stderr, "]\n"); + break; + case CDF_FILETIME: + tp = info[i].pi_tp; + if (tp < 1000000000000000LL) { + cdf_print_elapsed_time(buf, sizeof(buf), tp); + (void)fprintf(stderr, "timestamp %s\n", buf); + } else { + char buf[26]; + cdf_timestamp_to_timespec(&ts, tp); + (void)fprintf(stderr, "timestamp %s", + cdf_ctime(&ts.tv_sec, buf)); + } + break; + case CDF_CLIPBOARD: + (void)fprintf(stderr, "CLIPBOARD %u\n", info[i].pi_u32); + break; + default: + DPRINTF(("Don't know how to deal with %x\n", + info[i].pi_type)); + break; + } + } +} + + +void +cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst) +{ + char buf[128]; + cdf_summary_info_header_t ssi; + cdf_property_info_t *info; + size_t count; + + (void)&h; + if (cdf_unpack_summary_info(sst, h, &ssi, &info, &count) == -1) + return; + (void)fprintf(stderr, "Endian: %x\n", ssi.si_byte_order); + (void)fprintf(stderr, "Os Version %d.%d\n", ssi.si_os_version & 0xff, + ssi.si_os_version >> 8); + (void)fprintf(stderr, "Os %d\n", ssi.si_os); + cdf_print_classid(buf, sizeof(buf), &ssi.si_class); + (void)fprintf(stderr, "Class %s\n", buf); + (void)fprintf(stderr, "Count %d\n", ssi.si_count); + cdf_dump_property_info(info, count); + free(info); +} + +#endif + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + int i; + cdf_header_t h; + cdf_sat_t sat, ssat; + cdf_stream_t sst, scn; + cdf_dir_t dir; + cdf_info_t info; + + if (argc < 2) { + (void)fprintf(stderr, "Usage: %s <filename>\n", getprogname()); + return -1; + } + + info.i_buf = NULL; + info.i_len = 0; + for (i = 1; i < argc; i++) { + if ((info.i_fd = open(argv[1], O_RDONLY)) == -1) + err(1, "Cannot open `%s'", argv[1]); + + if (cdf_read_header(&info, &h) == -1) + err(1, "Cannot read header"); +#ifdef CDF_DEBUG + cdf_dump_header(&h); +#endif + + if (cdf_read_sat(&info, &h, &sat) == -1) + err(1, "Cannot read sat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h)); +#endif + + if (cdf_read_ssat(&info, &h, &sat, &ssat) == -1) + err(1, "Cannot read ssat"); +#ifdef CDF_DEBUG + cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h)); +#endif + + if (cdf_read_dir(&info, &h, &sat, &dir) == -1) + err(1, "Cannot read dir"); + + if (cdf_read_short_stream(&info, &h, &sat, &dir, &sst) == -1) + err(1, "Cannot read short stream"); +#ifdef CDF_DEBUG + cdf_dump_stream(&h, &sst); +#endif + +#ifdef CDF_DEBUG + cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir); +#endif + + + if (cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir, + &scn) == -1) + err(1, "Cannot read summary info"); +#ifdef CDF_DEBUG + cdf_dump_summary_info(&h, &scn); +#endif + + (void)close(info.i_fd); + } + + return 0; +} +#endif diff --git a/contrib/file/src/cdf.h b/contrib/file/src/cdf.h new file mode 100644 index 0000000..910fb95 --- /dev/null +++ b/contrib/file/src/cdf.h @@ -0,0 +1,329 @@ +/*- + * Copyright (c) 2008 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Parse Composite Document Files, the format used in Microsoft Office + * document files before they switched to zipped XML. + * Info from: http://sc.openoffice.org/compdocfileformat.pdf + * + * N.B. This is the "Composite Document File" format, and not the + * "Compound Document Format", nor the "Channel Definition Format". + */ + +#ifndef _H_CDF_ +#define _H_CDF_ + +#ifdef WIN32 +#include <winsock2.h> +#define timespec timeval +#define tv_nsec tv_usec +#endif +#ifdef __DJGPP__ +#define timespec timeval +#define tv_nsec tv_usec +#endif + +typedef int32_t cdf_secid_t; + +#define CDF_LOOP_LIMIT 10000 + +#define CDF_SECID_NULL 0 +#define CDF_SECID_FREE -1 +#define CDF_SECID_END_OF_CHAIN -2 +#define CDF_SECID_SECTOR_ALLOCATION_TABLE -3 +#define CDF_SECID_MASTER_SECTOR_ALLOCATION_TABLE -4 + +typedef struct { + uint64_t h_magic; +#define CDF_MAGIC 0xE11AB1A1E011CFD0LL + uint64_t h_uuid[2]; + uint16_t h_revision; + uint16_t h_version; + uint16_t h_byte_order; + uint16_t h_sec_size_p2; + uint16_t h_short_sec_size_p2; + uint8_t h_unused0[10]; + uint32_t h_num_sectors_in_sat; + uint32_t h_secid_first_directory; + uint8_t h_unused1[4]; + uint32_t h_min_size_standard_stream; + cdf_secid_t h_secid_first_sector_in_short_sat; + uint32_t h_num_sectors_in_short_sat; + cdf_secid_t h_secid_first_sector_in_master_sat; + uint32_t h_num_sectors_in_master_sat; + cdf_secid_t h_master_sat[436/4]; +} cdf_header_t; + +#define CDF_SEC_SIZE(h) ((size_t)(1 << (h)->h_sec_size_p2)) +#define CDF_SEC_POS(h, secid) (CDF_SEC_SIZE(h) + (secid) * CDF_SEC_SIZE(h)) +#define CDF_SHORT_SEC_SIZE(h) ((size_t)(1 << (h)->h_short_sec_size_p2)) +#define CDF_SHORT_SEC_POS(h, secid) ((secid) * CDF_SHORT_SEC_SIZE(h)) + +typedef int32_t cdf_dirid_t; +#define CDF_DIRID_NULL -1 + +typedef int64_t cdf_timestamp_t; +#define CDF_BASE_YEAR 1601 +#define CDF_TIME_PREC 10000000 + +typedef struct { + uint16_t d_name[32]; + uint16_t d_namelen; + uint8_t d_type; +#define CDF_DIR_TYPE_EMPTY 0 +#define CDF_DIR_TYPE_USER_STORAGE 1 +#define CDF_DIR_TYPE_USER_STREAM 2 +#define CDF_DIR_TYPE_LOCKBYTES 3 +#define CDF_DIR_TYPE_PROPERTY 4 +#define CDF_DIR_TYPE_ROOT_STORAGE 5 + uint8_t d_color; +#define CDF_DIR_COLOR_READ 0 +#define CDF_DIR_COLOR_BLACK 1 + cdf_dirid_t d_left_child; + cdf_dirid_t d_right_child; + cdf_dirid_t d_storage; + uint64_t d_storage_uuid[2]; + uint32_t d_flags; + cdf_timestamp_t d_created; + cdf_timestamp_t d_modified; + cdf_secid_t d_stream_first_sector; + uint32_t d_size; + uint32_t d_unused0; +} cdf_directory_t; + +#define CDF_DIRECTORY_SIZE 128 + +typedef struct { + cdf_secid_t *sat_tab; + size_t sat_len; +} cdf_sat_t; + +typedef struct { + cdf_directory_t *dir_tab; + size_t dir_len; +} cdf_dir_t; + +typedef struct { + void *sst_tab; + size_t sst_len; + size_t sst_dirlen; +} cdf_stream_t; + +typedef struct { + uint32_t cl_dword; + uint16_t cl_word[2]; + uint8_t cl_two[2]; + uint8_t cl_six[6]; +} cdf_classid_t; + +typedef struct { + uint16_t si_byte_order; + uint16_t si_zero; + uint16_t si_os_version; + uint16_t si_os; + cdf_classid_t si_class; + uint32_t si_count; +} cdf_summary_info_header_t; + +#define CDF_SECTION_DECLARATION_OFFSET 0x1c + +typedef struct { + cdf_classid_t sd_class; + uint32_t sd_offset; +} cdf_section_declaration_t; + +typedef struct { + uint32_t sh_len; + uint32_t sh_properties; +} cdf_section_header_t; + +typedef struct { + uint32_t pi_id; + uint32_t pi_type; + union { + uint16_t _pi_u16; + int16_t _pi_s16; + uint32_t _pi_u32; + int32_t _pi_s32; + uint64_t _pi_u64; + int64_t _pi_s64; + cdf_timestamp_t _pi_tp; + float _pi_f; + double _pi_d; + struct { + uint32_t s_len; + const char *s_buf; + } _pi_str; + } pi_val; +#define pi_u64 pi_val._pi_u64 +#define pi_s64 pi_val._pi_s64 +#define pi_u32 pi_val._pi_u32 +#define pi_s32 pi_val._pi_s32 +#define pi_u16 pi_val._pi_u16 +#define pi_s16 pi_val._pi_s16 +#define pi_f pi_val._pi_f +#define pi_d pi_val._pi_d +#define pi_tp pi_val._pi_tp +#define pi_str pi_val._pi_str +} cdf_property_info_t; + +#define CDF_ROUND(val, by) (((val) + (by) - 1) & ~((by) - 1)) + +/* Variant type definitions */ +#define CDF_EMPTY 0x00000000 +#define CDF_NULL 0x00000001 +#define CDF_SIGNED16 0x00000002 +#define CDF_SIGNED32 0x00000003 +#define CDF_FLOAT 0x00000004 +#define CDF_DOUBLE 0x00000005 +#define CDF_CY 0x00000006 +#define CDF_DATE 0x00000007 +#define CDF_BSTR 0x00000008 +#define CDF_DISPATCH 0x00000009 +#define CDF_ERROR 0x0000000a +#define CDF_BOOL 0x0000000b +#define CDF_VARIANT 0x0000000c +#define CDF_UNKNOWN 0x0000000d +#define CDF_DECIMAL 0x0000000e +#define CDF_SIGNED8 0x00000010 +#define CDF_UNSIGNED8 0x00000011 +#define CDF_UNSIGNED16 0x00000012 +#define CDF_UNSIGNED32 0x00000013 +#define CDF_SIGNED64 0x00000014 +#define CDF_UNSIGNED64 0x00000015 +#define CDF_INT 0x00000016 +#define CDF_UINT 0x00000017 +#define CDF_VOID 0x00000018 +#define CDF_HRESULT 0x00000019 +#define CDF_PTR 0x0000001a +#define CDF_SAFEARRAY 0x0000001b +#define CDF_CARRAY 0x0000001c +#define CDF_USERDEFINED 0x0000001d +#define CDF_LENGTH32_STRING 0x0000001e +#define CDF_LENGTH32_WSTRING 0x0000001f +#define CDF_FILETIME 0x00000040 +#define CDF_BLOB 0x00000041 +#define CDF_STREAM 0x00000042 +#define CDF_STORAGE 0x00000043 +#define CDF_STREAMED_OBJECT 0x00000044 +#define CDF_STORED_OBJECT 0x00000045 +#define CDF_BLOB_OBJECT 0x00000046 +#define CDF_CLIPBOARD 0x00000047 +#define CDF_CLSID 0x00000048 +#define CDF_VECTOR 0x00001000 +#define CDF_ARRAY 0x00002000 +#define CDF_BYREF 0x00004000 +#define CDF_RESERVED 0x00008000 +#define CDF_ILLEGAL 0x0000ffff +#define CDF_ILLEGALMASKED 0x00000fff +#define CDF_TYPEMASK 0x00000fff + +#define CDF_PROPERTY_CODE_PAGE 0x00000001 +#define CDF_PROPERTY_TITLE 0x00000002 +#define CDF_PROPERTY_SUBJECT 0x00000003 +#define CDF_PROPERTY_AUTHOR 0x00000004 +#define CDF_PROPERTY_KEYWORDS 0x00000005 +#define CDF_PROPERTY_COMMENTS 0x00000006 +#define CDF_PROPERTY_TEMPLATE 0x00000007 +#define CDF_PROPERTY_LAST_SAVED_BY 0x00000008 +#define CDF_PROPERTY_REVISION_NUMBER 0x00000009 +#define CDF_PROPERTY_TOTAL_EDITING_TIME 0x0000000a +#define CDF_PROPERTY_LAST_PRINTED 0X0000000b +#define CDF_PROPERTY_CREATE_TIME 0x0000000c +#define CDF_PROPERTY_LAST_SAVED_TIME 0x0000000d +#define CDF_PROPERTY_NUMBER_OF_PAGES 0x0000000e +#define CDF_PROPERTY_NUMBER_OF_WORDS 0x0000000f +#define CDF_PROPERTY_NUMBER_OF_CHARACTERS 0x00000010 +#define CDF_PROPERTY_THUMBNAIL 0x00000011 +#define CDF_PROPERTY_NAME_OF_APPLICATION 0x00000012 +#define CDF_PROPERTY_SECURITY 0x00000013 +#define CDF_PROPERTY_LOCALE_ID 0x80000000 + +typedef struct { + int i_fd; + const unsigned char *i_buf; + size_t i_len; +} cdf_info_t; + +struct timespec; +int cdf_timestamp_to_timespec(struct timespec *, cdf_timestamp_t); +int cdf_timespec_to_timestamp(cdf_timestamp_t *, const struct timespec *); +int cdf_read_header(const cdf_info_t *, cdf_header_t *); +void cdf_swap_header(cdf_header_t *); +void cdf_unpack_header(cdf_header_t *, char *); +void cdf_swap_dir(cdf_directory_t *); +void cdf_unpack_dir(cdf_directory_t *, char *); +void cdf_swap_class(cdf_classid_t *); +ssize_t cdf_read_sector(const cdf_info_t *, void *, size_t, size_t, + const cdf_header_t *, cdf_secid_t); +ssize_t cdf_read_short_sector(const cdf_stream_t *, void *, size_t, size_t, + const cdf_header_t *, cdf_secid_t); +int cdf_read_sat(const cdf_info_t *, cdf_header_t *, cdf_sat_t *); +size_t cdf_count_chain(const cdf_sat_t *, cdf_secid_t, size_t); +int cdf_read_long_sector_chain(const cdf_info_t *, const cdf_header_t *, + const cdf_sat_t *, cdf_secid_t, size_t, cdf_stream_t *); +int cdf_read_short_sector_chain(const cdf_header_t *, const cdf_sat_t *, + const cdf_stream_t *, cdf_secid_t, size_t, cdf_stream_t *); +int cdf_read_sector_chain(const cdf_info_t *, const cdf_header_t *, + const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *, cdf_secid_t, + size_t, cdf_stream_t *); +int cdf_read_dir(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *, + cdf_dir_t *); +int cdf_read_ssat(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *, + cdf_sat_t *); +int cdf_read_short_stream(const cdf_info_t *, const cdf_header_t *, + const cdf_sat_t *, const cdf_dir_t *, cdf_stream_t *, + const cdf_directory_t **); +int cdf_read_property_info(const cdf_stream_t *, const cdf_header_t *, uint32_t, + cdf_property_info_t **, size_t *, size_t *); +int cdf_read_user_stream(const cdf_info_t *, const cdf_header_t *, + const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *, + const cdf_dir_t *, const char *, cdf_stream_t *); +int cdf_read_summary_info(const cdf_info_t *, const cdf_header_t *, + const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *, + const cdf_dir_t *, cdf_stream_t *); +int cdf_unpack_summary_info(const cdf_stream_t *, const cdf_header_t *, + cdf_summary_info_header_t *, cdf_property_info_t **, size_t *); +int cdf_print_classid(char *, size_t, const cdf_classid_t *); +int cdf_print_property_name(char *, size_t, uint32_t); +int cdf_print_elapsed_time(char *, size_t, cdf_timestamp_t); +uint16_t cdf_tole2(uint16_t); +uint32_t cdf_tole4(uint32_t); +uint64_t cdf_tole8(uint64_t); +char *cdf_ctime(const time_t *, char *); + +#ifdef CDF_DEBUG +void cdf_dump_header(const cdf_header_t *); +void cdf_dump_sat(const char *, const cdf_sat_t *, size_t); +void cdf_dump(void *, size_t); +void cdf_dump_stream(const cdf_header_t *, const cdf_stream_t *); +void cdf_dump_dir(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *, + const cdf_sat_t *, const cdf_stream_t *, const cdf_dir_t *); +void cdf_dump_property_info(const cdf_property_info_t *, size_t); +void cdf_dump_summary_info(const cdf_header_t *, const cdf_stream_t *); +#endif + + +#endif /* _H_CDF_ */ diff --git a/contrib/file/src/cdf_time.c b/contrib/file/src/cdf_time.c new file mode 100644 index 0000000..1e572de --- /dev/null +++ b/contrib/file/src/cdf_time.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2008 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: cdf_time.c,v 1.15 2014/05/14 23:15:42 christos Exp $") +#endif + +#include <time.h> +#ifdef TEST +#include <err.h> +#endif +#include <string.h> + +#include "cdf.h" + +#define isleap(y) ((((y) % 4) == 0) && \ + ((((y) % 100) != 0) || (((y) % 400) == 0))) + +static const int mdays[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * Return the number of days between jan 01 1601 and jan 01 of year. + */ +static int +cdf_getdays(int year) +{ + int days = 0; + int y; + + for (y = CDF_BASE_YEAR; y < year; y++) + days += isleap(y) + 365; + + return days; +} + +/* + * Return the day within the month + */ +static int +cdf_getday(int year, int days) +{ + size_t m; + + for (m = 0; m < sizeof(mdays) / sizeof(mdays[0]); m++) { + int sub = mdays[m] + (m == 1 && isleap(year)); + if (days < sub) + return days; + days -= sub; + } + return days; +} + +/* + * Return the 0...11 month number. + */ +static int +cdf_getmonth(int year, int days) +{ + size_t m; + + for (m = 0; m < sizeof(mdays) / sizeof(mdays[0]); m++) { + days -= mdays[m]; + if (m == 1 && isleap(year)) + days--; + if (days <= 0) + return (int)m; + } + return (int)m; +} + +int +cdf_timestamp_to_timespec(struct timespec *ts, cdf_timestamp_t t) +{ + struct tm tm; +#ifdef HAVE_STRUCT_TM_TM_ZONE + static char UTC[] = "UTC"; +#endif + int rdays; + + /* Unit is 100's of nanoseconds */ + ts->tv_nsec = (t % CDF_TIME_PREC) * 100; + + t /= CDF_TIME_PREC; + tm.tm_sec = (int)(t % 60); + t /= 60; + + tm.tm_min = (int)(t % 60); + t /= 60; + + tm.tm_hour = (int)(t % 24); + t /= 24; + + /* XXX: Approx */ + tm.tm_year = (int)(CDF_BASE_YEAR + (t / 365)); + + rdays = cdf_getdays(tm.tm_year); + t -= rdays - 1; + tm.tm_mday = cdf_getday(tm.tm_year, (int)t); + tm.tm_mon = cdf_getmonth(tm.tm_year, (int)t); + tm.tm_wday = 0; + tm.tm_yday = 0; + tm.tm_isdst = 0; +#ifdef HAVE_STRUCT_TM_TM_GMTOFF + tm.tm_gmtoff = 0; +#endif +#ifdef HAVE_STRUCT_TM_TM_ZONE + tm.tm_zone = UTC; +#endif + tm.tm_year -= 1900; + ts->tv_sec = mktime(&tm); + if (ts->tv_sec == -1) { + errno = EINVAL; + return -1; + } + return 0; +} + +int +/*ARGSUSED*/ +cdf_timespec_to_timestamp(cdf_timestamp_t *t, const struct timespec *ts) +{ +#ifndef __lint__ + (void)&t; + (void)&ts; +#endif +#ifdef notyet + struct tm tm; + if (gmtime_r(&ts->ts_sec, &tm) == NULL) { + errno = EINVAL; + return -1; + } + *t = (ts->ts_nsec / 100) * CDF_TIME_PREC; + *t = tm.tm_sec; + *t += tm.tm_min * 60; + *t += tm.tm_hour * 60 * 60; + *t += tm.tm_mday * 60 * 60 * 24; +#endif + return 0; +} + +char * +cdf_ctime(const time_t *sec, char *buf) +{ + char *ptr = ctime_r(sec, buf); + if (ptr != NULL) + return buf; + (void)snprintf(buf, 26, "*Bad* 0x%16.16" INT64_T_FORMAT "x\n", + (long long)*sec); + return buf; +} + + +#ifdef TEST_TIME +int +main(int argc, char *argv[]) +{ + struct timespec ts; + char buf[25]; + static const cdf_timestamp_t tst = 0x01A5E403C2D59C00ULL; + static const char *ref = "Sat Apr 23 01:30:00 1977"; + char *p, *q; + + cdf_timestamp_to_timespec(&ts, tst); + p = cdf_ctime(&ts.tv_sec, buf); + if ((q = strchr(p, '\n')) != NULL) + *q = '\0'; + if (strcmp(ref, p) != 0) + errx(1, "Error date %s != %s\n", ref, p); + return 0; +} +#endif diff --git a/contrib/file/src/compress.c b/contrib/file/src/compress.c new file mode 100644 index 0000000..45251b1 --- /dev/null +++ b/contrib/file/src/compress.c @@ -0,0 +1,505 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * compress routines: + * zmagic() - returns 0 if not recognized, uncompresses and prints + * information if recognized + * uncompress(method, old, n, newch) - uncompress old into new, + * using method, return sizeof new + */ +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: compress.c,v 1.73 2014/01/05 15:55:21 christos Exp $") +#endif + +#include "magic.h" +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <string.h> +#include <errno.h> +#ifndef __MINGW32__ +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#if defined(HAVE_SYS_TIME_H) +#include <sys/time.h> +#endif +#if defined(HAVE_ZLIB_H) && defined(HAVE_LIBZ) +#define BUILTIN_DECOMPRESS +#include <zlib.h> +#endif + +private const struct { + const char magic[8]; + size_t maglen; + const char *argv[3]; + int silent; +} compr[] = { + { "\037\235", 2, { "gzip", "-cdq", NULL }, 1 }, /* compressed */ + /* Uncompress can get stuck; so use gzip first if we have it + * Idea from Damien Clark, thanks! */ + { "\037\235", 2, { "uncompress", "-c", NULL }, 1 }, /* compressed */ + { "\037\213", 2, { "gzip", "-cdq", NULL }, 1 }, /* gzipped */ + { "\037\236", 2, { "gzip", "-cdq", NULL }, 1 }, /* frozen */ + { "\037\240", 2, { "gzip", "-cdq", NULL }, 1 }, /* SCO LZH */ + /* the standard pack utilities do not accept standard input */ + { "\037\036", 2, { "gzip", "-cdq", NULL }, 0 }, /* packed */ + { "PK\3\4", 4, { "gzip", "-cdq", NULL }, 1 }, /* pkzipped, */ + /* ...only first file examined */ + { "BZh", 3, { "bzip2", "-cd", NULL }, 1 }, /* bzip2-ed */ + { "LZIP", 4, { "lzip", "-cdq", NULL }, 1 }, + { "\3757zXZ\0",6,{ "xz", "-cd", NULL }, 1 }, /* XZ Utils */ + { "LRZI", 4, { "lrzip", "-dqo-", NULL }, 1 }, /* LRZIP */ + { "\004\"M\030", 4, { "lz4", "-cd", NULL }, 1 }, /* LZ4 */ +}; + +#define NODATA ((size_t)~0) + +private ssize_t swrite(int, const void *, size_t); +#if HAVE_FORK +private size_t ncompr = sizeof(compr) / sizeof(compr[0]); +private size_t uncompressbuf(struct magic_set *, int, size_t, + const unsigned char *, unsigned char **, size_t); +#ifdef BUILTIN_DECOMPRESS +private size_t uncompressgzipped(struct magic_set *, const unsigned char *, + unsigned char **, size_t); +#endif + +protected int +file_zmagic(struct magic_set *ms, int fd, const char *name, + const unsigned char *buf, size_t nbytes) +{ + unsigned char *newbuf = NULL; + size_t i, nsz; + int rv = 0; + int mime = ms->flags & MAGIC_MIME; + + if ((ms->flags & MAGIC_COMPRESS) == 0) + return 0; + + for (i = 0; i < ncompr; i++) { + if (nbytes < compr[i].maglen) + continue; + if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 && + (nsz = uncompressbuf(ms, fd, i, buf, &newbuf, + nbytes)) != NODATA) { + ms->flags &= ~MAGIC_COMPRESS; + rv = -1; + if (file_buffer(ms, -1, name, newbuf, nsz) == -1) + goto error; + + if (mime == MAGIC_MIME || mime == 0) { + if (file_printf(ms, mime ? + " compressed-encoding=" : " (") == -1) + goto error; + if (file_buffer(ms, -1, NULL, buf, nbytes) == -1) + goto error; + if (!mime && file_printf(ms, ")") == -1) + goto error; + } + + rv = 1; + break; + } + } +error: + free(newbuf); + ms->flags |= MAGIC_COMPRESS; + return rv; +} +#endif +/* + * `safe' write for sockets and pipes. + */ +private ssize_t +swrite(int fd, const void *buf, size_t n) +{ + ssize_t rv; + size_t rn = n; + + do + switch (rv = write(fd, buf, n)) { + case -1: + if (errno == EINTR) + continue; + return -1; + default: + n -= rv; + buf = CAST(const char *, buf) + rv; + break; + } + while (n > 0); + return rn; +} + + +/* + * `safe' read for sockets and pipes. + */ +protected ssize_t +sread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__))) +{ + ssize_t rv; +#ifdef FIONREAD + int t = 0; +#endif + size_t rn = n; + + if (fd == STDIN_FILENO) + goto nocheck; + +#ifdef FIONREAD + if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) { +#ifdef FD_ZERO + ssize_t cnt; + for (cnt = 0;; cnt++) { + fd_set check; + struct timeval tout = {0, 100 * 1000}; + int selrv; + + FD_ZERO(&check); + FD_SET(fd, &check); + + /* + * Avoid soft deadlock: do not read if there + * is nothing to read from sockets and pipes. + */ + selrv = select(fd + 1, &check, NULL, NULL, &tout); + if (selrv == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + } else if (selrv == 0 && cnt >= 5) { + return 0; + } else + break; + } +#endif + (void)ioctl(fd, FIONREAD, &t); + } + + if (t > 0 && (size_t)t < n) { + n = t; + rn = n; + } +#endif + +nocheck: + do + switch ((rv = read(fd, buf, n))) { + case -1: + if (errno == EINTR) + continue; + return -1; + case 0: + return rn - n; + default: + n -= rv; + buf = ((char *)buf) + rv; + break; + } + while (n > 0); + return rn; +} + +protected int +file_pipe2file(struct magic_set *ms, int fd, const void *startbuf, + size_t nbytes) +{ + char buf[4096]; + ssize_t r; + int tfd; + + (void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf); +#ifndef HAVE_MKSTEMP + { + char *ptr = mktemp(buf); + tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600); + r = errno; + (void)unlink(ptr); + errno = r; + } +#else + { + int te; + tfd = mkstemp(buf); + te = errno; + (void)unlink(buf); + errno = te; + } +#endif + if (tfd == -1) { + file_error(ms, errno, + "cannot create temporary file for pipe copy"); + return -1; + } + + if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes) + r = 1; + else { + while ((r = sread(fd, buf, sizeof(buf), 1)) > 0) + if (swrite(tfd, buf, (size_t)r) != r) + break; + } + + switch (r) { + case -1: + file_error(ms, errno, "error copying from pipe to temp file"); + return -1; + case 0: + break; + default: + file_error(ms, errno, "error while writing to temp file"); + return -1; + } + + /* + * We duplicate the file descriptor, because fclose on a + * tmpfile will delete the file, but any open descriptors + * can still access the phantom inode. + */ + if ((fd = dup2(tfd, fd)) == -1) { + file_error(ms, errno, "could not dup descriptor for temp file"); + return -1; + } + (void)close(tfd); + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { + file_badseek(ms); + return -1; + } + return fd; +} +#if HAVE_FORK +#ifdef BUILTIN_DECOMPRESS + +#define FHCRC (1 << 1) +#define FEXTRA (1 << 2) +#define FNAME (1 << 3) +#define FCOMMENT (1 << 4) + +private size_t +uncompressgzipped(struct magic_set *ms, const unsigned char *old, + unsigned char **newch, size_t n) +{ + unsigned char flg = old[3]; + size_t data_start = 10; + z_stream z; + int rc; + + if (flg & FEXTRA) { + if (data_start+1 >= n) + return 0; + data_start += 2 + old[data_start] + old[data_start + 1] * 256; + } + if (flg & FNAME) { + while(data_start < n && old[data_start]) + data_start++; + data_start++; + } + if(flg & FCOMMENT) { + while(data_start < n && old[data_start]) + data_start++; + data_start++; + } + if(flg & FHCRC) + data_start += 2; + + if (data_start >= n) + return 0; + if ((*newch = CAST(unsigned char *, malloc(HOWMANY + 1))) == NULL) { + return 0; + } + + /* XXX: const castaway, via strchr */ + z.next_in = (Bytef *)strchr((const char *)old + data_start, + old[data_start]); + z.avail_in = CAST(uint32_t, (n - data_start)); + z.next_out = *newch; + z.avail_out = HOWMANY; + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + + /* LINTED bug in header macro */ + rc = inflateInit2(&z, -15); + if (rc != Z_OK) { + file_error(ms, 0, "zlib: %s", z.msg); + return 0; + } + + rc = inflate(&z, Z_SYNC_FLUSH); + if (rc != Z_OK && rc != Z_STREAM_END) { + file_error(ms, 0, "zlib: %s", z.msg); + return 0; + } + + n = (size_t)z.total_out; + (void)inflateEnd(&z); + + /* let's keep the nul-terminate tradition */ + (*newch)[n] = '\0'; + + return n; +} +#endif + +private size_t +uncompressbuf(struct magic_set *ms, int fd, size_t method, + const unsigned char *old, unsigned char **newch, size_t n) +{ + int fdin[2], fdout[2]; + ssize_t r; + pid_t pid; + +#ifdef BUILTIN_DECOMPRESS + /* FIXME: This doesn't cope with bzip2 */ + if (method == 2) + return uncompressgzipped(ms, old, newch, n); +#endif + (void)fflush(stdout); + (void)fflush(stderr); + + if ((fd != -1 && pipe(fdin) == -1) || pipe(fdout) == -1) { + file_error(ms, errno, "cannot create pipe"); + return NODATA; + } + switch (pid = fork()) { + case 0: /* child */ + (void) close(0); + if (fd != -1) { + if (dup(fd) == -1) + _exit(1); + (void) lseek(0, (off_t)0, SEEK_SET); + } else { + if (dup(fdin[0]) == -1) + _exit(1); + (void) close(fdin[0]); + (void) close(fdin[1]); + } + + (void) close(1); + if (dup(fdout[1]) == -1) + _exit(1); + (void) close(fdout[0]); + (void) close(fdout[1]); +#ifndef DEBUG + if (compr[method].silent) + (void)close(2); +#endif + + (void)execvp(compr[method].argv[0], + (char *const *)(intptr_t)compr[method].argv); +#ifdef DEBUG + (void)fprintf(stderr, "exec `%s' failed (%s)\n", + compr[method].argv[0], strerror(errno)); +#endif + exit(1); + /*NOTREACHED*/ + case -1: + file_error(ms, errno, "could not fork"); + return NODATA; + + default: /* parent */ + (void) close(fdout[1]); + if (fd == -1) { + (void) close(fdin[0]); + /* + * fork again, to avoid blocking because both + * pipes filled + */ + switch (fork()) { + case 0: /* child */ + (void)close(fdout[0]); + if (swrite(fdin[1], old, n) != (ssize_t)n) { +#ifdef DEBUG + (void)fprintf(stderr, + "Write failed (%s)\n", + strerror(errno)); +#endif + exit(1); + } + exit(0); + /*NOTREACHED*/ + + case -1: +#ifdef DEBUG + (void)fprintf(stderr, "Fork failed (%s)\n", + strerror(errno)); +#endif + exit(1); + /*NOTREACHED*/ + + default: /* parent */ + break; + } + (void) close(fdin[1]); + fdin[1] = -1; + } + + if ((*newch = (unsigned char *) malloc(HOWMANY + 1)) == NULL) { +#ifdef DEBUG + (void)fprintf(stderr, "Malloc failed (%s)\n", + strerror(errno)); +#endif + n = 0; + goto err; + } + if ((r = sread(fdout[0], *newch, HOWMANY, 0)) <= 0) { +#ifdef DEBUG + (void)fprintf(stderr, "Read failed (%s)\n", + strerror(errno)); +#endif + free(*newch); + n = 0; + *newch = NULL; + goto err; + } else { + n = r; + } + /* NUL terminate, as every buffer is handled here. */ + (*newch)[n] = '\0'; +err: + if (fdin[1] != -1) + (void) close(fdin[1]); + (void) close(fdout[0]); +#ifdef WNOHANG + while (waitpid(pid, NULL, WNOHANG) != -1) + continue; +#else + (void)wait(NULL); +#endif + (void) close(fdin[0]); + + return n; + } +} +#endif diff --git a/contrib/file/src/ctime_r.c b/contrib/file/src/ctime_r.c new file mode 100644 index 0000000..ca7ad2f --- /dev/null +++ b/contrib/file/src/ctime_r.c @@ -0,0 +1,19 @@ +/* $File: ctime_r.c,v 1.1 2012/05/15 17:14:36 christos Exp $ */ + +#include "file.h" +#ifndef lint +FILE_RCSID("@(#)$File: ctime_r.c,v 1.1 2012/05/15 17:14:36 christos Exp $") +#endif /* lint */ +#include <time.h> +#include <string.h> + +/* ctime_r is not thread-safe anyway */ +char * +ctime_r(const time_t *t, char *dst) +{ + char *p = ctime(t); + if (p == NULL) + return NULL; + memcpy(dst, p, 26); + return dst; +} diff --git a/contrib/file/src/elfclass.h b/contrib/file/src/elfclass.h new file mode 100644 index 0000000..010958a --- /dev/null +++ b/contrib/file/src/elfclass.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) Christos Zoulas 2008. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + if (nbytes <= sizeof(elfhdr)) + return 0; + + u.l = 1; + (void)memcpy(&elfhdr, buf, sizeof elfhdr); + swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[EI_DATA]; + + type = elf_getu16(swap, elfhdr.e_type); + switch (type) { +#ifdef ELFCORE + case ET_CORE: + flags |= FLAGS_IS_CORE; + if (dophn_core(ms, clazz, swap, fd, + (off_t)elf_getu(swap, elfhdr.e_phoff), + elf_getu16(swap, elfhdr.e_phnum), + (size_t)elf_getu16(swap, elfhdr.e_phentsize), + fsize, &flags) == -1) + return -1; + break; +#endif + case ET_EXEC: + case ET_DYN: + if (dophn_exec(ms, clazz, swap, fd, + (off_t)elf_getu(swap, elfhdr.e_phoff), + elf_getu16(swap, elfhdr.e_phnum), + (size_t)elf_getu16(swap, elfhdr.e_phentsize), + fsize, &flags, elf_getu16(swap, elfhdr.e_shnum)) + == -1) + return -1; + /*FALLTHROUGH*/ + case ET_REL: + if (doshn(ms, clazz, swap, fd, + (off_t)elf_getu(swap, elfhdr.e_shoff), + elf_getu16(swap, elfhdr.e_shnum), + (size_t)elf_getu16(swap, elfhdr.e_shentsize), + fsize, &flags, elf_getu16(swap, elfhdr.e_machine), + (int)elf_getu16(swap, elfhdr.e_shstrndx)) == -1) + return -1; + break; + + default: + break; + } + return 1; diff --git a/contrib/file/src/encoding.c b/contrib/file/src/encoding.c new file mode 100644 index 0000000..b75e0cc --- /dev/null +++ b/contrib/file/src/encoding.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Encoding -- determine the character encoding of a text file. + * + * Joerg Wunsch <joerg@freebsd.org> wrote the original support for 8-bit + * international characters. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: encoding.c,v 1.9 2013/11/19 20:45:50 christos Exp $") +#endif /* lint */ + +#include "magic.h" +#include <string.h> +#include <memory.h> +#include <stdlib.h> + + +private int looks_ascii(const unsigned char *, size_t, unichar *, size_t *); +private int looks_utf8_with_BOM(const unsigned char *, size_t, unichar *, + size_t *); +private int looks_ucs16(const unsigned char *, size_t, unichar *, size_t *); +private int looks_latin1(const unsigned char *, size_t, unichar *, size_t *); +private int looks_extended(const unsigned char *, size_t, unichar *, size_t *); +private void from_ebcdic(const unsigned char *, size_t, unsigned char *); + +#ifdef DEBUG_ENCODING +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +/* + * Try to determine whether text is in some character code we can + * identify. Each of these tests, if it succeeds, will leave + * the text converted into one-unichar-per-character Unicode in + * ubuf, and the number of characters converted in ulen. + */ +protected int +file_encoding(struct magic_set *ms, const unsigned char *buf, size_t nbytes, unichar **ubuf, size_t *ulen, const char **code, const char **code_mime, const char **type) +{ + size_t mlen; + int rv = 1, ucs_type; + unsigned char *nbuf = NULL; + + *type = "text"; + *ulen = 0; + *code = "unknown"; + *code_mime = "binary"; + + mlen = (nbytes + 1) * sizeof((*ubuf)[0]); + if ((*ubuf = CAST(unichar *, calloc((size_t)1, mlen))) == NULL) { + file_oomem(ms, mlen); + goto done; + } + mlen = (nbytes + 1) * sizeof(nbuf[0]); + if ((nbuf = CAST(unsigned char *, calloc((size_t)1, mlen))) == NULL) { + file_oomem(ms, mlen); + goto done; + } + + if (looks_ascii(buf, nbytes, *ubuf, ulen)) { + DPRINTF(("ascii %" SIZE_T_FORMAT "u\n", *ulen)); + *code = "ASCII"; + *code_mime = "us-ascii"; + } else if (looks_utf8_with_BOM(buf, nbytes, *ubuf, ulen) > 0) { + DPRINTF(("utf8/bom %" SIZE_T_FORMAT "u\n", *ulen)); + *code = "UTF-8 Unicode (with BOM)"; + *code_mime = "utf-8"; + } else if (file_looks_utf8(buf, nbytes, *ubuf, ulen) > 1) { + DPRINTF(("utf8 %" SIZE_T_FORMAT "u\n", *ulen)); + *code = "UTF-8 Unicode (with BOM)"; + *code = "UTF-8 Unicode"; + *code_mime = "utf-8"; + } else if ((ucs_type = looks_ucs16(buf, nbytes, *ubuf, ulen)) != 0) { + if (ucs_type == 1) { + *code = "Little-endian UTF-16 Unicode"; + *code_mime = "utf-16le"; + } else { + *code = "Big-endian UTF-16 Unicode"; + *code_mime = "utf-16be"; + } + DPRINTF(("ucs16 %" SIZE_T_FORMAT "u\n", *ulen)); + } else if (looks_latin1(buf, nbytes, *ubuf, ulen)) { + DPRINTF(("latin1 %" SIZE_T_FORMAT "u\n", *ulen)); + *code = "ISO-8859"; + *code_mime = "iso-8859-1"; + } else if (looks_extended(buf, nbytes, *ubuf, ulen)) { + DPRINTF(("extended %" SIZE_T_FORMAT "u\n", *ulen)); + *code = "Non-ISO extended-ASCII"; + *code_mime = "unknown-8bit"; + } else { + from_ebcdic(buf, nbytes, nbuf); + + if (looks_ascii(nbuf, nbytes, *ubuf, ulen)) { + DPRINTF(("ebcdic %" SIZE_T_FORMAT "u\n", *ulen)); + *code = "EBCDIC"; + *code_mime = "ebcdic"; + } else if (looks_latin1(nbuf, nbytes, *ubuf, ulen)) { + DPRINTF(("ebcdic/international %" SIZE_T_FORMAT "u\n", + *ulen)); + *code = "International EBCDIC"; + *code_mime = "ebcdic"; + } else { /* Doesn't look like text at all */ + DPRINTF(("binary\n")); + rv = 0; + *type = "binary"; + } + } + + done: + free(nbuf); + + return rv; +} + +/* + * This table reflects a particular philosophy about what constitutes + * "text," and there is room for disagreement about it. + * + * Version 3.31 of the file command considered a file to be ASCII if + * each of its characters was approved by either the isascii() or + * isalpha() function. On most systems, this would mean that any + * file consisting only of characters in the range 0x00 ... 0x7F + * would be called ASCII text, but many systems might reasonably + * consider some characters outside this range to be alphabetic, + * so the file command would call such characters ASCII. It might + * have been more accurate to call this "considered textual on the + * local system" than "ASCII." + * + * It considered a file to be "International language text" if each + * of its characters was either an ASCII printing character (according + * to the real ASCII standard, not the above test), a character in + * the range 0x80 ... 0xFF, or one of the following control characters: + * backspace, tab, line feed, vertical tab, form feed, carriage return, + * escape. No attempt was made to determine the language in which files + * of this type were written. + * + * + * The table below considers a file to be ASCII if all of its characters + * are either ASCII printing characters (again, according to the X3.4 + * standard, not isascii()) or any of the following controls: bell, + * backspace, tab, line feed, form feed, carriage return, esc, nextline. + * + * I include bell because some programs (particularly shell scripts) + * use it literally, even though it is rare in normal text. I exclude + * vertical tab because it never seems to be used in real text. I also + * include, with hesitation, the X3.64/ECMA-43 control nextline (0x85), + * because that's what the dd EBCDIC->ASCII table maps the EBCDIC newline + * character to. It might be more appropriate to include it in the 8859 + * set instead of the ASCII set, but it's got to be included in *something* + * we recognize or EBCDIC files aren't going to be considered textual. + * Some old Unix source files use SO/SI (^N/^O) to shift between Greek + * and Latin characters, so these should possibly be allowed. But they + * make a real mess on VT100-style displays if they're not paired properly, + * so we are probably better off not calling them text. + * + * A file is considered to be ISO-8859 text if its characters are all + * either ASCII, according to the above definition, or printing characters + * from the ISO-8859 8-bit extension, characters 0xA0 ... 0xFF. + * + * Finally, a file is considered to be international text from some other + * character code if its characters are all either ISO-8859 (according to + * the above definition) or characters in the range 0x80 ... 0x9F, which + * ISO-8859 considers to be control characters but the IBM PC and Macintosh + * consider to be printing characters. + */ + +#define F 0 /* character never appears in text */ +#define T 1 /* character appears in plain ASCII text */ +#define I 2 /* character appears in ISO-8859 text */ +#define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */ + +private char text_chars[256] = { + /* BEL BS HT LF FF CR */ + F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */ + /* ESC */ + F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */ + /* NEL */ + X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */ + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I /* 0xfX */ +}; + +private int +looks_ascii(const unsigned char *buf, size_t nbytes, unichar *ubuf, + size_t *ulen) +{ + size_t i; + + *ulen = 0; + + for (i = 0; i < nbytes; i++) { + int t = text_chars[buf[i]]; + + if (t != T) + return 0; + + ubuf[(*ulen)++] = buf[i]; + } + + return 1; +} + +private int +looks_latin1(const unsigned char *buf, size_t nbytes, unichar *ubuf, size_t *ulen) +{ + size_t i; + + *ulen = 0; + + for (i = 0; i < nbytes; i++) { + int t = text_chars[buf[i]]; + + if (t != T && t != I) + return 0; + + ubuf[(*ulen)++] = buf[i]; + } + + return 1; +} + +private int +looks_extended(const unsigned char *buf, size_t nbytes, unichar *ubuf, + size_t *ulen) +{ + size_t i; + + *ulen = 0; + + for (i = 0; i < nbytes; i++) { + int t = text_chars[buf[i]]; + + if (t != T && t != I && t != X) + return 0; + + ubuf[(*ulen)++] = buf[i]; + } + + return 1; +} + +/* + * Decide whether some text looks like UTF-8. Returns: + * + * -1: invalid UTF-8 + * 0: uses odd control characters, so doesn't look like text + * 1: 7-bit text + * 2: definitely UTF-8 text (valid high-bit set bytes) + * + * If ubuf is non-NULL on entry, text is decoded into ubuf, *ulen; + * ubuf must be big enough! + */ +protected int +file_looks_utf8(const unsigned char *buf, size_t nbytes, unichar *ubuf, size_t *ulen) +{ + size_t i; + int n; + unichar c; + int gotone = 0, ctrl = 0; + + if (ubuf) + *ulen = 0; + + for (i = 0; i < nbytes; i++) { + if ((buf[i] & 0x80) == 0) { /* 0xxxxxxx is plain ASCII */ + /* + * Even if the whole file is valid UTF-8 sequences, + * still reject it if it uses weird control characters. + */ + + if (text_chars[buf[i]] != T) + ctrl = 1; + + if (ubuf) + ubuf[(*ulen)++] = buf[i]; + } else if ((buf[i] & 0x40) == 0) { /* 10xxxxxx never 1st byte */ + return -1; + } else { /* 11xxxxxx begins UTF-8 */ + int following; + + if ((buf[i] & 0x20) == 0) { /* 110xxxxx */ + c = buf[i] & 0x1f; + following = 1; + } else if ((buf[i] & 0x10) == 0) { /* 1110xxxx */ + c = buf[i] & 0x0f; + following = 2; + } else if ((buf[i] & 0x08) == 0) { /* 11110xxx */ + c = buf[i] & 0x07; + following = 3; + } else if ((buf[i] & 0x04) == 0) { /* 111110xx */ + c = buf[i] & 0x03; + following = 4; + } else if ((buf[i] & 0x02) == 0) { /* 1111110x */ + c = buf[i] & 0x01; + following = 5; + } else + return -1; + + for (n = 0; n < following; n++) { + i++; + if (i >= nbytes) + goto done; + + if ((buf[i] & 0x80) == 0 || (buf[i] & 0x40)) + return -1; + + c = (c << 6) + (buf[i] & 0x3f); + } + + if (ubuf) + ubuf[(*ulen)++] = c; + gotone = 1; + } + } +done: + return ctrl ? 0 : (gotone ? 2 : 1); +} + +/* + * Decide whether some text looks like UTF-8 with BOM. If there is no + * BOM, return -1; otherwise return the result of looks_utf8 on the + * rest of the text. + */ +private int +looks_utf8_with_BOM(const unsigned char *buf, size_t nbytes, unichar *ubuf, + size_t *ulen) +{ + if (nbytes > 3 && buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf) + return file_looks_utf8(buf + 3, nbytes - 3, ubuf, ulen); + else + return -1; +} + +private int +looks_ucs16(const unsigned char *buf, size_t nbytes, unichar *ubuf, + size_t *ulen) +{ + int bigend; + size_t i; + + if (nbytes < 2) + return 0; + + if (buf[0] == 0xff && buf[1] == 0xfe) + bigend = 0; + else if (buf[0] == 0xfe && buf[1] == 0xff) + bigend = 1; + else + return 0; + + *ulen = 0; + + for (i = 2; i + 1 < nbytes; i += 2) { + /* XXX fix to properly handle chars > 65536 */ + + if (bigend) + ubuf[(*ulen)++] = buf[i + 1] + 256 * buf[i]; + else + ubuf[(*ulen)++] = buf[i] + 256 * buf[i + 1]; + + if (ubuf[*ulen - 1] == 0xfffe) + return 0; + if (ubuf[*ulen - 1] < 128 && + text_chars[(size_t)ubuf[*ulen - 1]] != T) + return 0; + } + + return 1 + bigend; +} + +#undef F +#undef T +#undef I +#undef X + +/* + * This table maps each EBCDIC character to an (8-bit extended) ASCII + * character, as specified in the rationale for the dd(1) command in + * draft 11.2 (September, 1991) of the POSIX P1003.2 standard. + * + * Unfortunately it does not seem to correspond exactly to any of the + * five variants of EBCDIC documented in IBM's _Enterprise Systems + * Architecture/390: Principles of Operation_, SA22-7201-06, Seventh + * Edition, July, 1999, pp. I-1 - I-4. + * + * Fortunately, though, all versions of EBCDIC, including this one, agree + * on most of the printing characters that also appear in (7-bit) ASCII. + * Of these, only '|', '!', '~', '^', '[', and ']' are in question at all. + * + * Fortunately too, there is general agreement that codes 0x00 through + * 0x3F represent control characters, 0x41 a nonbreaking space, and the + * remainder printing characters. + * + * This is sufficient to allow us to identify EBCDIC text and to distinguish + * between old-style and internationalized examples of text. + */ + +private unsigned char ebcdic_to_ascii[] = { + 0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, +128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, +144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, +' ', 160, 161, 162, 163, 164, 165, 166, 167, 168, 213, '.', '<', '(', '+', '|', +'&', 169, 170, 171, 172, 173, 174, 175, 176, 177, '!', '$', '*', ')', ';', '~', +'-', '/', 178, 179, 180, 181, 182, 183, 184, 185, 203, ',', '%', '_', '>', '?', +186, 187, 188, 189, 190, 191, 192, 193, 194, '`', ':', '#', '@', '\'','=', '"', +195, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 196, 197, 198, 199, 200, 201, +202, 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', '^', 204, 205, 206, 207, 208, +209, 229, 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 210, 211, 212, '[', 214, 215, +216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, ']', 230, 231, +'{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 232, 233, 234, 235, 236, 237, +'}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 238, 239, 240, 241, 242, 243, +'\\',159, 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 244, 245, 246, 247, 248, 249, +'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 250, 251, 252, 253, 254, 255 +}; + +#ifdef notdef +/* + * The following EBCDIC-to-ASCII table may relate more closely to reality, + * or at least to modern reality. It comes from + * + * http://ftp.s390.ibm.com/products/oe/bpxqp9.html + * + * and maps the characters of EBCDIC code page 1047 (the code used for + * Unix-derived software on IBM's 390 systems) to the corresponding + * characters from ISO 8859-1. + * + * If this table is used instead of the above one, some of the special + * cases for the NEL character can be taken out of the code. + */ + +private unsigned char ebcdic_1047_to_8859[] = { +0x00,0x01,0x02,0x03,0x9C,0x09,0x86,0x7F,0x97,0x8D,0x8E,0x0B,0x0C,0x0D,0x0E,0x0F, +0x10,0x11,0x12,0x13,0x9D,0x0A,0x08,0x87,0x18,0x19,0x92,0x8F,0x1C,0x1D,0x1E,0x1F, +0x80,0x81,0x82,0x83,0x84,0x85,0x17,0x1B,0x88,0x89,0x8A,0x8B,0x8C,0x05,0x06,0x07, +0x90,0x91,0x16,0x93,0x94,0x95,0x96,0x04,0x98,0x99,0x9A,0x9B,0x14,0x15,0x9E,0x1A, +0x20,0xA0,0xE2,0xE4,0xE0,0xE1,0xE3,0xE5,0xE7,0xF1,0xA2,0x2E,0x3C,0x28,0x2B,0x7C, +0x26,0xE9,0xEA,0xEB,0xE8,0xED,0xEE,0xEF,0xEC,0xDF,0x21,0x24,0x2A,0x29,0x3B,0x5E, +0x2D,0x2F,0xC2,0xC4,0xC0,0xC1,0xC3,0xC5,0xC7,0xD1,0xA6,0x2C,0x25,0x5F,0x3E,0x3F, +0xF8,0xC9,0xCA,0xCB,0xC8,0xCD,0xCE,0xCF,0xCC,0x60,0x3A,0x23,0x40,0x27,0x3D,0x22, +0xD8,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0xAB,0xBB,0xF0,0xFD,0xFE,0xB1, +0xB0,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0xAA,0xBA,0xE6,0xB8,0xC6,0xA4, +0xB5,0x7E,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0xA1,0xBF,0xD0,0x5B,0xDE,0xAE, +0xAC,0xA3,0xA5,0xB7,0xA9,0xA7,0xB6,0xBC,0xBD,0xBE,0xDD,0xA8,0xAF,0x5D,0xB4,0xD7, +0x7B,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0xAD,0xF4,0xF6,0xF2,0xF3,0xF5, +0x7D,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0xB9,0xFB,0xFC,0xF9,0xFA,0xFF, +0x5C,0xF7,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0xB2,0xD4,0xD6,0xD2,0xD3,0xD5, +0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0xB3,0xDB,0xDC,0xD9,0xDA,0x9F +}; +#endif + +/* + * Copy buf[0 ... nbytes-1] into out[], translating EBCDIC to ASCII. + */ +private void +from_ebcdic(const unsigned char *buf, size_t nbytes, unsigned char *out) +{ + size_t i; + + for (i = 0; i < nbytes; i++) { + out[i] = ebcdic_to_ascii[buf[i]]; + } +} diff --git a/contrib/file/src/file.c b/contrib/file/src/file.c new file mode 100644 index 0000000..370da91 --- /dev/null +++ b/contrib/file/src/file.c @@ -0,0 +1,540 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * file - find type of a file or files - main program. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: file.c,v 1.153 2014/02/11 15:41:04 christos Exp $") +#endif /* lint */ + +#include "magic.h" + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#ifdef RESTORE_TIME +# if (__COHERENT__ >= 0x420) +# include <sys/utime.h> +# else +# ifdef USE_UTIMES +# include <sys/time.h> +# else +# include <utime.h> +# endif +# endif +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for read() */ +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#ifdef HAVE_WCHAR_H +#include <wchar.h> +#endif + +#if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION) +#include <getopt.h> +#ifndef HAVE_GETOPT_LONG +int getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex); +#endif +#else +#include "mygetopt.h" +#endif + +#ifdef S_IFLNK +#define FILE_FLAGS "-bcEhikLlNnprsvz0" +#else +#define FILE_FLAGS "-bcEiklNnprsvz0" +#endif + +# define USAGE \ + "Usage: %s [" FILE_FLAGS \ + "] [--apple] [--mime-encoding] [--mime-type]\n" \ + " [-e testname] [-F separator] [-f namefile] [-m magicfiles] " \ + "file ...\n" \ + " %s -C [-m magicfiles]\n" \ + " %s [--help]\n" + +private int /* Global command-line options */ + bflag = 0, /* brief output format */ + nopad = 0, /* Don't pad output */ + nobuffer = 0, /* Do not buffer stdout */ + nulsep = 0; /* Append '\0' to the separator */ + +private const char *separator = ":"; /* Default field separator */ +private const struct option long_options[] = { +#define OPT(shortname, longname, opt, doc) \ + {longname, opt, NULL, shortname}, +#define OPT_LONGONLY(longname, opt, doc) \ + {longname, opt, NULL, 0}, +#include "file_opts.h" +#undef OPT +#undef OPT_LONGONLY + {0, 0, NULL, 0} +}; +#define OPTSTRING "bcCde:Ef:F:hiklLm:nNprsvz0" + +private const struct { + const char *name; + int value; +} nv[] = { + { "apptype", MAGIC_NO_CHECK_APPTYPE }, + { "ascii", MAGIC_NO_CHECK_ASCII }, + { "cdf", MAGIC_NO_CHECK_CDF }, + { "compress", MAGIC_NO_CHECK_COMPRESS }, + { "elf", MAGIC_NO_CHECK_ELF }, + { "encoding", MAGIC_NO_CHECK_ENCODING }, + { "soft", MAGIC_NO_CHECK_SOFT }, + { "tar", MAGIC_NO_CHECK_TAR }, + { "text", MAGIC_NO_CHECK_TEXT }, /* synonym for ascii */ + { "tokens", MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */ +}; + +private char *progname; /* used throughout */ + +private void usage(void); +private void docprint(const char *); +private void help(void); + +private int unwrap(struct magic_set *, const char *); +private int process(struct magic_set *ms, const char *, int); +private struct magic_set *load(const char *, int); + + +/* + * main - parse arguments and handle options + */ +int +main(int argc, char *argv[]) +{ + int c; + size_t i; + int action = 0, didsomefiles = 0, errflg = 0; + int flags = 0, e = 0; + struct magic_set *magic = NULL; + int longindex; + const char *magicfile = NULL; /* where the magic is */ + + /* makes islower etc work for other langs */ + (void)setlocale(LC_CTYPE, ""); + +#ifdef __EMX__ + /* sh-like wildcard expansion! Shouldn't hurt at least ... */ + _wildcard(&argc, &argv); +#endif + + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; + +#ifdef S_IFLNK + flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0; +#endif + while ((c = getopt_long(argc, argv, OPTSTRING, long_options, + &longindex)) != -1) + switch (c) { + case 0 : + switch (longindex) { + case 0: + help(); + break; + case 10: + flags |= MAGIC_APPLE; + break; + case 11: + flags |= MAGIC_MIME_TYPE; + break; + case 12: + flags |= MAGIC_MIME_ENCODING; + break; + } + break; + case '0': + nulsep = 1; + break; + case 'b': + bflag++; + break; + case 'c': + action = FILE_CHECK; + break; + case 'C': + action = FILE_COMPILE; + break; + case 'd': + flags |= MAGIC_DEBUG|MAGIC_CHECK; + break; + case 'E': + flags |= MAGIC_ERROR; + break; + case 'e': + for (i = 0; i < sizeof(nv) / sizeof(nv[0]); i++) + if (strcmp(nv[i].name, optarg) == 0) + break; + + if (i == sizeof(nv) / sizeof(nv[0])) + errflg++; + else + flags |= nv[i].value; + break; + + case 'f': + if(action) + usage(); + if (magic == NULL) + if ((magic = load(magicfile, flags)) == NULL) + return 1; + e |= unwrap(magic, optarg); + ++didsomefiles; + break; + case 'F': + separator = optarg; + break; + case 'i': + flags |= MAGIC_MIME; + break; + case 'k': + flags |= MAGIC_CONTINUE; + break; + case 'l': + action = FILE_LIST; + break; + case 'm': + magicfile = optarg; + break; + case 'n': + ++nobuffer; + break; + case 'N': + ++nopad; + break; +#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) + case 'p': + flags |= MAGIC_PRESERVE_ATIME; + break; +#endif + case 'r': + flags |= MAGIC_RAW; + break; + case 's': + flags |= MAGIC_DEVICES; + break; + case 'v': + if (magicfile == NULL) + magicfile = magic_getpath(magicfile, action); + (void)fprintf(stdout, "%s-%s\n", progname, VERSION); + (void)fprintf(stdout, "magic file from %s\n", + magicfile); + return 0; + case 'z': + flags |= MAGIC_COMPRESS; + break; +#ifdef S_IFLNK + case 'L': + flags |= MAGIC_SYMLINK; + break; + case 'h': + flags &= ~MAGIC_SYMLINK; + break; +#endif + case '?': + default: + errflg++; + break; + } + + if (errflg) { + usage(); + } + if (e) + return e; + + if (MAGIC_VERSION != magic_version()) + (void)fprintf(stderr, "%s: compiled magic version [%d] " + "does not match with shared library magic version [%d]\n", + progname, MAGIC_VERSION, magic_version()); + + switch(action) { + case FILE_CHECK: + case FILE_COMPILE: + case FILE_LIST: + /* + * Don't try to check/compile ~/.magic unless we explicitly + * ask for it. + */ + magic = magic_open(flags|MAGIC_CHECK); + if (magic == NULL) { + (void)fprintf(stderr, "%s: %s\n", progname, + strerror(errno)); + return 1; + } + switch(action) { + case FILE_CHECK: + c = magic_check(magic, magicfile); + break; + case FILE_COMPILE: + c = magic_compile(magic, magicfile); + break; + case FILE_LIST: + c = magic_list(magic, magicfile); + break; + default: + abort(); + } + if (c == -1) { + (void)fprintf(stderr, "%s: %s\n", progname, + magic_error(magic)); + return 1; + } + return 0; + default: + if (magic == NULL) + if ((magic = load(magicfile, flags)) == NULL) + return 1; + break; + } + + if (optind == argc) { + if (!didsomefiles) + usage(); + } + else { + size_t j, wid, nw; + for (wid = 0, j = (size_t)optind; j < (size_t)argc; j++) { + nw = file_mbswidth(argv[j]); + if (nw > wid) + wid = nw; + } + /* + * If bflag is only set twice, set it depending on + * number of files [this is undocumented, and subject to change] + */ + if (bflag == 2) { + bflag = optind >= argc - 1; + } + for (; optind < argc; optind++) + e |= process(magic, argv[optind], wid); + } + + if (magic) + magic_close(magic); + return e; +} + + +private struct magic_set * +/*ARGSUSED*/ +load(const char *magicfile, int flags) +{ + struct magic_set *magic = magic_open(flags); + if (magic == NULL) { + (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + return NULL; + } + if (magic_load(magic, magicfile) == -1) { + (void)fprintf(stderr, "%s: %s\n", + progname, magic_error(magic)); + magic_close(magic); + return NULL; + } + return magic; +} + +/* + * unwrap -- read a file of filenames, do each one. + */ +private int +unwrap(struct magic_set *ms, const char *fn) +{ + FILE *f; + ssize_t len; + char *line = NULL; + size_t llen = 0; + int wid = 0, cwid; + int e = 0; + + if (strcmp("-", fn) == 0) { + f = stdin; + wid = 1; + } else { + if ((f = fopen(fn, "r")) == NULL) { + (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n", + progname, fn, strerror(errno)); + return 1; + } + + while ((len = getline(&line, &llen, f)) > 0) { + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + cwid = file_mbswidth(line); + if (cwid > wid) + wid = cwid; + } + + rewind(f); + } + + while ((len = getline(&line, &llen, f)) > 0) { + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + e |= process(ms, line, wid); + if(nobuffer) + (void)fflush(stdout); + } + + free(line); + (void)fclose(f); + return e; +} + +/* + * Called for each input file on the command line (or in a list of files) + */ +private int +process(struct magic_set *ms, const char *inname, int wid) +{ + const char *type; + int std_in = strcmp(inname, "-") == 0; + + if (wid > 0 && !bflag) { + (void)printf("%s", std_in ? "/dev/stdin" : inname); + if (nulsep) + (void)putc('\0', stdout); + (void)printf("%s", separator); + (void)printf("%*s ", + (int) (nopad ? 0 : (wid - file_mbswidth(inname))), ""); + } + + type = magic_file(ms, std_in ? NULL : inname); + if (type == NULL) { + (void)printf("ERROR: %s\n", magic_error(ms)); + return 1; + } else { + (void)printf("%s\n", type); + return 0; + } +} + +protected size_t +file_mbswidth(const char *s) +{ +#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) + size_t bytesconsumed, old_n, n, width = 0; + mbstate_t state; + wchar_t nextchar; + (void)memset(&state, 0, sizeof(mbstate_t)); + old_n = n = strlen(s); + + while (n > 0) { + bytesconsumed = mbrtowc(&nextchar, s, n, &state); + if (bytesconsumed == (size_t)(-1) || + bytesconsumed == (size_t)(-2)) { + /* Something went wrong, return something reasonable */ + return old_n; + } + if (s[0] == '\n') { + /* + * do what strlen() would do, so that caller + * is always right + */ + width++; + } else { + int w = wcwidth(nextchar); + if (w > 0) + width += w; + } + + s += bytesconsumed, n -= bytesconsumed; + } + return width; +#else + return strlen(s); +#endif +} + +private void +usage(void) +{ + (void)fprintf(stderr, USAGE, progname, progname, progname); + exit(1); +} + +private void +docprint(const char *opts) +{ + size_t i; + int comma; + char *sp, *p; + + p = strstr(opts, "%o"); + if (p == NULL) { + fprintf(stdout, "%s", opts); + return; + } + + for (sp = p - 1; sp > opts && *sp == ' '; sp--) + continue; + + fprintf(stdout, "%.*s", (int)(p - opts), opts); + + comma = 0; + for (i = 0; i < __arraycount(nv); i++) { + fprintf(stdout, "%s%s", comma++ ? ", " : "", nv[i].name); + if (i && i % 5 == 0) { + fprintf(stdout, ",\n%*s", (int)(p - sp - 1), ""); + comma = 0; + } + } + + fprintf(stdout, "%s", opts + (p - opts) + 2); +} + +private void +help(void) +{ + (void)fputs( +"Usage: file [OPTION...] [FILE...]\n" +"Determine type of FILEs.\n" +"\n", stdout); +#define OPT(shortname, longname, opt, doc) \ + fprintf(stdout, " -%c, --" longname, shortname), \ + docprint(doc); +#define OPT_LONGONLY(longname, opt, doc) \ + fprintf(stdout, " --" longname), \ + docprint(doc); +#include "file_opts.h" +#undef OPT +#undef OPT_LONGONLY + fprintf(stdout, "\nReport bugs to http://bugs.gw.com/\n"); + exit(0); +} diff --git a/contrib/file/src/file.h b/contrib/file/src/file.h new file mode 100644 index 0000000..67a2568 --- /dev/null +++ b/contrib/file/src/file.h @@ -0,0 +1,557 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * file.h - definitions for file(1) program + * @(#)$File: file.h,v 1.152 2014/06/03 19:01:34 christos Exp $ + */ + +#ifndef __file_h__ +#define __file_h__ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WIN32 + #ifdef _WIN64 + #define SIZE_T_FORMAT "I64" + #else + #define SIZE_T_FORMAT "" + #endif + #define INT64_T_FORMAT "I64" +#else + #define SIZE_T_FORMAT "z" + #define INT64_T_FORMAT "ll" +#endif + +#include <stdio.h> /* Include that here, to make sure __P gets defined */ +#include <errno.h> +#include <fcntl.h> /* For open and flags */ +#ifdef HAVE_STDINT_H +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#include <stdint.h> +#endif +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif +#include <regex.h> +#include <time.h> +#include <sys/types.h> +#include <sys/param.h> +/* Do this here and now, because struct stat gets re-defined on solaris */ +#include <sys/stat.h> +#include <stdarg.h> + +#define ENABLE_CONDITIONALS + +#ifndef MAGIC +#define MAGIC "/etc/magic" +#endif + +#if defined(__EMX__) || defined (WIN32) +#define PATHSEP ';' +#else +#define PATHSEP ':' +#endif + +#define private static + +#if HAVE_VISIBILITY && !defined(WIN32) +#define public __attribute__ ((__visibility__("default"))) +#ifndef protected +#define protected __attribute__ ((__visibility__("hidden"))) +#endif +#else +#define public +#ifndef protected +#define protected +#endif +#endif + +#ifndef __arraycount +#define __arraycount(a) (sizeof(a) / sizeof(a[0])) +#endif + +#ifndef __GNUC_PREREQ__ +#ifdef __GNUC__ +#define __GNUC_PREREQ__(x, y) \ + ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ + (__GNUC__ > (x))) +#else +#define __GNUC_PREREQ__(x, y) 0 +#endif +#endif + +#ifndef __GNUC__ +#ifndef __attribute__ +#define __attribute__(a) +#endif +#endif + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef HOWMANY +# define HOWMANY (256 * 1024) /* how much of the file to look at */ +#endif +#define MAXMAGIS 8192 /* max entries in any one magic file + or directory */ +#define MAXDESC 64 /* max len of text description/MIME type */ +#define MAXMIME 80 /* max len of text MIME type */ +#define MAXstring 64 /* max len of "string" types */ + +#define MAGICNO 0xF11E041C +#define VERSIONNO 12 +#define FILE_MAGICSIZE 248 + +#define FILE_LOAD 0 +#define FILE_CHECK 1 +#define FILE_COMPILE 2 +#define FILE_LIST 3 + +union VALUETYPE { + uint8_t b; + uint16_t h; + uint32_t l; + uint64_t q; + uint8_t hs[2]; /* 2 bytes of a fixed-endian "short" */ + uint8_t hl[4]; /* 4 bytes of a fixed-endian "long" */ + uint8_t hq[8]; /* 8 bytes of a fixed-endian "quad" */ + char s[MAXstring]; /* the search string or regex pattern */ + unsigned char us[MAXstring]; + float f; + double d; +}; + +struct magic { + /* Word 1 */ + uint16_t cont_level; /* level of ">" */ + uint8_t flag; +#define INDIR 0x01 /* if '(...)' appears */ +#define OFFADD 0x02 /* if '>&' or '>...(&' appears */ +#define INDIROFFADD 0x04 /* if '>&(' appears */ +#define UNSIGNED 0x08 /* comparison is unsigned */ +#define NOSPACE 0x10 /* suppress space character before output */ +#define BINTEST 0x20 /* test is for a binary type (set only + for top-level tests) */ +#define TEXTTEST 0x40 /* for passing to file_softmagic */ + + uint8_t factor; + + /* Word 2 */ + uint8_t reln; /* relation (0=eq, '>'=gt, etc) */ + uint8_t vallen; /* length of string value, if any */ + uint8_t type; /* comparison type (FILE_*) */ + uint8_t in_type; /* type of indirection */ +#define FILE_INVALID 0 +#define FILE_BYTE 1 +#define FILE_SHORT 2 +#define FILE_DEFAULT 3 +#define FILE_LONG 4 +#define FILE_STRING 5 +#define FILE_DATE 6 +#define FILE_BESHORT 7 +#define FILE_BELONG 8 +#define FILE_BEDATE 9 +#define FILE_LESHORT 10 +#define FILE_LELONG 11 +#define FILE_LEDATE 12 +#define FILE_PSTRING 13 +#define FILE_LDATE 14 +#define FILE_BELDATE 15 +#define FILE_LELDATE 16 +#define FILE_REGEX 17 +#define FILE_BESTRING16 18 +#define FILE_LESTRING16 19 +#define FILE_SEARCH 20 +#define FILE_MEDATE 21 +#define FILE_MELDATE 22 +#define FILE_MELONG 23 +#define FILE_QUAD 24 +#define FILE_LEQUAD 25 +#define FILE_BEQUAD 26 +#define FILE_QDATE 27 +#define FILE_LEQDATE 28 +#define FILE_BEQDATE 29 +#define FILE_QLDATE 30 +#define FILE_LEQLDATE 31 +#define FILE_BEQLDATE 32 +#define FILE_FLOAT 33 +#define FILE_BEFLOAT 34 +#define FILE_LEFLOAT 35 +#define FILE_DOUBLE 36 +#define FILE_BEDOUBLE 37 +#define FILE_LEDOUBLE 38 +#define FILE_BEID3 39 +#define FILE_LEID3 40 +#define FILE_INDIRECT 41 +#define FILE_QWDATE 42 +#define FILE_LEQWDATE 43 +#define FILE_BEQWDATE 44 +#define FILE_NAME 45 +#define FILE_USE 46 +#define FILE_CLEAR 47 +#define FILE_NAMES_SIZE 48 /* size of array to contain all names */ + +#define IS_STRING(t) \ + ((t) == FILE_STRING || \ + (t) == FILE_PSTRING || \ + (t) == FILE_BESTRING16 || \ + (t) == FILE_LESTRING16 || \ + (t) == FILE_REGEX || \ + (t) == FILE_SEARCH || \ + (t) == FILE_NAME || \ + (t) == FILE_USE) + +#define FILE_FMT_NONE 0 +#define FILE_FMT_NUM 1 /* "cduxXi" */ +#define FILE_FMT_STR 2 /* "s" */ +#define FILE_FMT_QUAD 3 /* "ll" */ +#define FILE_FMT_FLOAT 4 /* "eEfFgG" */ +#define FILE_FMT_DOUBLE 5 /* "eEfFgG" */ + + /* Word 3 */ + uint8_t in_op; /* operator for indirection */ + uint8_t mask_op; /* operator for mask */ +#ifdef ENABLE_CONDITIONALS + uint8_t cond; /* conditional type */ +#else + uint8_t dummy; +#endif + uint8_t factor_op; +#define FILE_FACTOR_OP_PLUS '+' +#define FILE_FACTOR_OP_MINUS '-' +#define FILE_FACTOR_OP_TIMES '*' +#define FILE_FACTOR_OP_DIV '/' +#define FILE_FACTOR_OP_NONE '\0' + +#define FILE_OPS "&|^+-*/%" +#define FILE_OPAND 0 +#define FILE_OPOR 1 +#define FILE_OPXOR 2 +#define FILE_OPADD 3 +#define FILE_OPMINUS 4 +#define FILE_OPMULTIPLY 5 +#define FILE_OPDIVIDE 6 +#define FILE_OPMODULO 7 +#define FILE_OPS_MASK 0x07 /* mask for above ops */ +#define FILE_UNUSED_1 0x08 +#define FILE_UNUSED_2 0x10 +#define FILE_UNUSED_3 0x20 +#define FILE_OPINVERSE 0x40 +#define FILE_OPINDIRECT 0x80 + +#ifdef ENABLE_CONDITIONALS +#define COND_NONE 0 +#define COND_IF 1 +#define COND_ELIF 2 +#define COND_ELSE 3 +#endif /* ENABLE_CONDITIONALS */ + + /* Word 4 */ + uint32_t offset; /* offset to magic number */ + /* Word 5 */ + int32_t in_offset; /* offset from indirection */ + /* Word 6 */ + uint32_t lineno; /* line number in magic file */ + /* Word 7,8 */ + union { + uint64_t _mask; /* for use with numeric and date types */ + struct { + uint32_t _count; /* repeat/line count */ + uint32_t _flags; /* modifier flags */ + } _s; /* for use with string types */ + } _u; +#define num_mask _u._mask +#define str_range _u._s._count +#define str_flags _u._s._flags + /* Words 9-16 */ + union VALUETYPE value; /* either number or string */ + /* Words 17-32 */ + char desc[MAXDESC]; /* description */ + /* Words 33-52 */ + char mimetype[MAXMIME]; /* MIME type */ + /* Words 53-54 */ + char apple[8]; +}; + +#define BIT(A) (1 << (A)) +#define STRING_COMPACT_WHITESPACE BIT(0) +#define STRING_COMPACT_OPTIONAL_WHITESPACE BIT(1) +#define STRING_IGNORE_LOWERCASE BIT(2) +#define STRING_IGNORE_UPPERCASE BIT(3) +#define REGEX_OFFSET_START BIT(4) +#define STRING_TEXTTEST BIT(5) +#define STRING_BINTEST BIT(6) +#define PSTRING_1_BE BIT(7) +#define PSTRING_1_LE BIT(7) +#define PSTRING_2_BE BIT(8) +#define PSTRING_2_LE BIT(9) +#define PSTRING_4_BE BIT(10) +#define PSTRING_4_LE BIT(11) +#define REGEX_LINE_COUNT BIT(11) +#define PSTRING_LEN \ + (PSTRING_1_BE|PSTRING_2_LE|PSTRING_2_BE|PSTRING_4_LE|PSTRING_4_BE) +#define PSTRING_LENGTH_INCLUDES_ITSELF BIT(12) +#define STRING_TRIM BIT(13) +#define CHAR_COMPACT_WHITESPACE 'W' +#define CHAR_COMPACT_OPTIONAL_WHITESPACE 'w' +#define CHAR_IGNORE_LOWERCASE 'c' +#define CHAR_IGNORE_UPPERCASE 'C' +#define CHAR_REGEX_OFFSET_START 's' +#define CHAR_TEXTTEST 't' +#define CHAR_TRIM 'T' +#define CHAR_BINTEST 'b' +#define CHAR_PSTRING_1_BE 'B' +#define CHAR_PSTRING_1_LE 'B' +#define CHAR_PSTRING_2_BE 'H' +#define CHAR_PSTRING_2_LE 'h' +#define CHAR_PSTRING_4_BE 'L' +#define CHAR_PSTRING_4_LE 'l' +#define CHAR_PSTRING_LENGTH_INCLUDES_ITSELF 'J' +#define STRING_IGNORE_CASE (STRING_IGNORE_LOWERCASE|STRING_IGNORE_UPPERCASE) +#define STRING_DEFAULT_RANGE 100 + + +/* list of magic entries */ +struct mlist { + struct magic *magic; /* array of magic entries */ + uint32_t nmagic; /* number of entries in array */ + void *map; /* internal resources used by entry */ + struct mlist *next, *prev; +}; + +#ifdef __cplusplus +#define CAST(T, b) static_cast<T>(b) +#define RCAST(T, b) reinterpret_cast<T>(b) +#else +#define CAST(T, b) (T)(b) +#define RCAST(T, b) (T)(b) +#endif + +struct level_info { + int32_t off; + int got_match; +#ifdef ENABLE_CONDITIONALS + int last_match; + int last_cond; /* used for error checking by parse() */ +#endif +}; + +#define MAGIC_SETS 2 + +struct magic_set { + struct mlist *mlist[MAGIC_SETS]; /* list of regular entries */ + struct cont { + size_t len; + struct level_info *li; + } c; + struct out { + char *buf; /* Accumulation buffer */ + char *pbuf; /* Printable buffer */ + } o; + uint32_t offset; + int error; + int flags; /* Control magic tests. */ + int event_flags; /* Note things that happened. */ +#define EVENT_HAD_ERR 0x01 + const char *file; + size_t line; /* current magic line number */ + + /* data for searches */ + struct { + const char *s; /* start of search in original source */ + size_t s_len; /* length of search region */ + size_t offset; /* starting offset in source: XXX - should this be off_t? */ + size_t rm_len; /* match length */ + } search; + + /* FIXME: Make the string dynamically allocated so that e.g. + strings matched in files can be longer than MAXstring */ + union VALUETYPE ms_value; /* either number or string */ +}; + +/* Type for Unicode characters */ +typedef unsigned long unichar; + +struct stat; +#define FILE_T_LOCAL 1 +#define FILE_T_WINDOWS 2 +protected const char *file_fmttime(uint64_t, int, char *); +protected struct magic_set *file_ms_alloc(int); +protected void file_ms_free(struct magic_set *); +protected int file_buffer(struct magic_set *, int, const char *, const void *, + size_t); +protected int file_fsmagic(struct magic_set *, const char *, struct stat *); +protected int file_pipe2file(struct magic_set *, int, const void *, size_t); +protected int file_vprintf(struct magic_set *, const char *, va_list) + __attribute__((__format__(__printf__, 2, 0))); +protected size_t file_printedlen(const struct magic_set *); +protected int file_replace(struct magic_set *, const char *, const char *); +protected int file_printf(struct magic_set *, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +protected int file_reset(struct magic_set *); +protected int file_tryelf(struct magic_set *, int, const unsigned char *, + size_t); +protected int file_trycdf(struct magic_set *, int, const unsigned char *, + size_t); +#if HAVE_FORK +protected int file_zmagic(struct magic_set *, int, const char *, + const unsigned char *, size_t); +#endif +protected int file_ascmagic(struct magic_set *, const unsigned char *, size_t, + int); +protected int file_ascmagic_with_encoding(struct magic_set *, + const unsigned char *, size_t, unichar *, size_t, const char *, + const char *, int); +protected int file_encoding(struct magic_set *, const unsigned char *, size_t, + unichar **, size_t *, const char **, const char **, const char **); +protected int file_is_tar(struct magic_set *, const unsigned char *, size_t); +protected int file_softmagic(struct magic_set *, const unsigned char *, size_t, + size_t, int, int); +protected int file_apprentice(struct magic_set *, const char *, int); +protected int file_magicfind(struct magic_set *, const char *, struct mlist *); +protected uint64_t file_signextend(struct magic_set *, struct magic *, + uint64_t); +protected void file_badread(struct magic_set *); +protected void file_badseek(struct magic_set *); +protected void file_oomem(struct magic_set *, size_t); +protected void file_error(struct magic_set *, int, const char *, ...) + __attribute__((__format__(__printf__, 3, 4))); +protected void file_magerror(struct magic_set *, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +protected void file_magwarn(struct magic_set *, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +protected void file_mdump(struct magic *); +protected void file_showstr(FILE *, const char *, size_t); +protected size_t file_mbswidth(const char *); +protected const char *file_getbuffer(struct magic_set *); +protected ssize_t sread(int, void *, size_t, int); +protected int file_check_mem(struct magic_set *, unsigned int); +protected int file_looks_utf8(const unsigned char *, size_t, unichar *, + size_t *); +protected size_t file_pstring_length_size(const struct magic *); +protected size_t file_pstring_get_length(const struct magic *, const char *); +#ifdef __EMX__ +protected int file_os2_apptype(struct magic_set *, const char *, const void *, + size_t); +#endif /* __EMX__ */ + +typedef struct { + const char *pat; + char *old_lc_ctype; + int rc; + regex_t rx; +} file_regex_t; + +protected int file_regcomp(file_regex_t *, const char *, int); +protected int file_regexec(file_regex_t *, const char *, size_t, regmatch_t *, + int); +protected void file_regfree(file_regex_t *); +protected void file_regerror(file_regex_t *, int, struct magic_set *); + +#ifndef COMPILE_ONLY +extern const char *file_names[]; +extern const size_t file_nnames; +#endif + +#ifndef HAVE_STRERROR +extern int sys_nerr; +extern char *sys_errlist[]; +#define strerror(e) \ + (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error") +#endif + +#ifndef HAVE_STRTOUL +#define strtoul(a, b, c) strtol(a, b, c) +#endif + +#ifndef HAVE_PREAD +ssize_t pread(int, void *, size_t, off_t); +#endif +#ifndef HAVE_VASPRINTF +int vasprintf(char **, const char *, va_list); +#endif +#ifndef HAVE_ASPRINTF +int asprintf(char **, const char *, ...); +#endif + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *, const char *, size_t); +#endif +#ifndef HAVE_STRLCAT +size_t strlcat(char *, const char *, size_t); +#endif +#ifndef HAVE_STRCASESTR +char *strcasestr(const char *, const char *); +#endif +#ifndef HAVE_GETLINE +ssize_t getline(char **, size_t *, FILE *); +ssize_t getdelim(char **, size_t *, int, FILE *); +#endif +#ifndef HAVE_CTIME_R +char *ctime_r(const time_t *, char *); +#endif +#ifndef HAVE_ASCTIME_R +char *asctime_r(const struct tm *, char *); +#endif +#ifndef HAVE_FMTCHECK +const char *fmtcheck(const char *, const char *) + __attribute__((__format_arg__(2))); +#endif + +#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H) && !defined(QUICK) +#define QUICK +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifndef __cplusplus +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define FILE_RCSID(id) \ +static const char rcsid[] __attribute__((__used__)) = id; +#else +#define FILE_RCSID(id) \ +static const char *rcsid(const char *p) { \ + return rcsid(p = id); \ +} +#endif +#else +#define FILE_RCSID(id) +#endif + +#endif /* __file_h__ */ diff --git a/contrib/file/src/file_opts.h b/contrib/file/src/file_opts.h new file mode 100644 index 0000000..db34eb7 --- /dev/null +++ b/contrib/file/src/file_opts.h @@ -0,0 +1,50 @@ +/* + * Table of command-line options + * + * The first column specifies the short name, if any, or 0 if none. + * The second column specifies the long name. + * The third column specifies whether it takes a parameter. + * The fourth column is the documentation. + * + * N.B. The long options' order must correspond to the code in file.c, + * and OPTSTRING must be kept up-to-date with the short options. + * Pay particular attention to the numbers of long-only options in the + * switch statement! + */ + +OPT_LONGONLY("help", 0, " display this help and exit\n") +OPT('v', "version", 0, " output version information and exit\n") +OPT('m', "magic-file", 1, " LIST use LIST as a colon-separated list of magic\n" + " number files\n") +OPT('z', "uncompress", 0, " try to look inside compressed files\n") +OPT('b', "brief", 0, " do not prepend filenames to output lines\n") +OPT('c', "checking-printout", 0, " print the parsed form of the magic file, use in\n" + " conjunction with -m to debug a new magic file\n" + " before installing it\n") +OPT('e', "exclude", 1, " TEST exclude TEST from the list of test to be\n" + " performed for file. Valid tests are:\n" + " %o\n") +OPT('f', "files-from", 1, " FILE read the filenames to be examined from FILE\n") +OPT('F', "separator", 1, " STRING use string as separator instead of `:'\n") +OPT('i', "mime", 0, " output MIME type strings (--mime-type and\n" + " --mime-encoding)\n") +OPT_LONGONLY("apple", 0, " output the Apple CREATOR/TYPE\n") +OPT_LONGONLY("mime-type", 0, " output the MIME type\n") +OPT_LONGONLY("mime-encoding", 0, " output the MIME encoding\n") +OPT('k', "keep-going", 0, " don't stop at the first match\n") +OPT('l', "list", 0, " list magic strength\n") +#ifdef S_IFLNK +OPT('L', "dereference", 0, " follow symlinks (default)\n") +OPT('h', "no-dereference", 0, " don't follow symlinks\n") +#endif +OPT('n', "no-buffer", 0, " do not buffer output\n") +OPT('N', "no-pad", 0, " do not pad output\n") +OPT('0', "print0", 0, " terminate filenames with ASCII NUL\n") +#if defined(HAVE_UTIME) || defined(HAVE_UTIMES) +OPT('p', "preserve-date", 0, " preserve access times on files\n") +#endif +OPT('r', "raw", 0, " don't translate unprintable chars to \\ooo\n") +OPT('s', "special-files", 0, " treat special (block/char devices) files as\n" + " ordinary ones\n") +OPT('C', "compile", 0, " compile file specified by -m\n") +OPT('d', "debug", 0, " print debugging messages\n") diff --git a/contrib/file/src/fmtcheck.c b/contrib/file/src/fmtcheck.c new file mode 100644 index 0000000..0fc7038 --- /dev/null +++ b/contrib/file/src/fmtcheck.c @@ -0,0 +1,234 @@ +/* $NetBSD: fmtcheck.c,v 1.8 2008/04/28 20:22:59 martin Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Allen Briggs. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "file.h" + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +enum __e_fmtcheck_types { + FMTCHECK_START, + FMTCHECK_SHORT, + FMTCHECK_INT, + FMTCHECK_LONG, + FMTCHECK_QUAD, + FMTCHECK_SHORTPOINTER, + FMTCHECK_INTPOINTER, + FMTCHECK_LONGPOINTER, + FMTCHECK_QUADPOINTER, + FMTCHECK_DOUBLE, + FMTCHECK_LONGDOUBLE, + FMTCHECK_STRING, + FMTCHECK_WIDTH, + FMTCHECK_PRECISION, + FMTCHECK_DONE, + FMTCHECK_UNKNOWN +}; +typedef enum __e_fmtcheck_types EFT; + +#define RETURN(pf,f,r) do { \ + *(pf) = (f); \ + return r; \ + } /*NOTREACHED*/ /*CONSTCOND*/ while (0) + +static EFT +get_next_format_from_precision(const char **pf) +{ + int sh, lg, quad, longdouble; + const char *f; + + sh = lg = quad = longdouble = 0; + + f = *pf; + switch (*f) { + case 'h': + f++; + sh = 1; + break; + case 'l': + f++; + if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); + if (*f == 'l') { + f++; + quad = 1; + } else { + lg = 1; + } + break; + case 'q': + f++; + quad = 1; + break; + case 'L': + f++; + longdouble = 1; + break; + default: + break; + } + if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); + if (strchr("diouxX", *f)) { + if (longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + if (lg) + RETURN(pf,f,FMTCHECK_LONG); + if (quad) + RETURN(pf,f,FMTCHECK_QUAD); + RETURN(pf,f,FMTCHECK_INT); + } + if (*f == 'n') { + if (longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + if (sh) + RETURN(pf,f,FMTCHECK_SHORTPOINTER); + if (lg) + RETURN(pf,f,FMTCHECK_LONGPOINTER); + if (quad) + RETURN(pf,f,FMTCHECK_QUADPOINTER); + RETURN(pf,f,FMTCHECK_INTPOINTER); + } + if (strchr("DOU", *f)) { + if (sh + lg + quad + longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_LONG); + } + if (strchr("eEfg", *f)) { + if (longdouble) + RETURN(pf,f,FMTCHECK_LONGDOUBLE); + if (sh + lg + quad) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_DOUBLE); + } + if (*f == 'c') { + if (sh + lg + quad + longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_INT); + } + if (*f == 's') { + if (sh + lg + quad + longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_STRING); + } + if (*f == 'p') { + if (sh + lg + quad + longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_LONG); + } + RETURN(pf,f,FMTCHECK_UNKNOWN); + /*NOTREACHED*/ +} + +static EFT +get_next_format_from_width(const char **pf) +{ + const char *f; + + f = *pf; + if (*f == '.') { + f++; + if (*f == '*') { + RETURN(pf,f,FMTCHECK_PRECISION); + } + /* eat any precision (empty is allowed) */ + while (isdigit((unsigned char)*f)) f++; + if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); + } + RETURN(pf,f,get_next_format_from_precision(pf)); + /*NOTREACHED*/ +} + +static EFT +get_next_format(const char **pf, EFT eft) +{ + int infmt; + const char *f; + + if (eft == FMTCHECK_WIDTH) { + (*pf)++; + return get_next_format_from_width(pf); + } else if (eft == FMTCHECK_PRECISION) { + (*pf)++; + return get_next_format_from_precision(pf); + } + + f = *pf; + infmt = 0; + while (!infmt) { + f = strchr(f, '%'); + if (f == NULL) + RETURN(pf,f,FMTCHECK_DONE); + f++; + if (!*f) + RETURN(pf,f,FMTCHECK_UNKNOWN); + if (*f != '%') + infmt = 1; + else + f++; + } + + /* Eat any of the flags */ + while (*f && (strchr("#0- +", *f))) + f++; + + if (*f == '*') { + RETURN(pf,f,FMTCHECK_WIDTH); + } + /* eat any width */ + while (isdigit((unsigned char)*f)) f++; + if (!*f) { + RETURN(pf,f,FMTCHECK_UNKNOWN); + } + + RETURN(pf,f,get_next_format_from_width(pf)); + /*NOTREACHED*/ +} + +const char * +fmtcheck(const char *f1, const char *f2) +{ + const char *f1p, *f2p; + EFT f1t, f2t; + + if (!f1) return f2; + + f1p = f1; + f1t = FMTCHECK_START; + f2p = f2; + f2t = FMTCHECK_START; + while ((f1t = get_next_format(&f1p, f1t)) != FMTCHECK_DONE) { + if (f1t == FMTCHECK_UNKNOWN) + return f2; + f2t = get_next_format(&f2p, f2t); + if (f1t != f2t) + return f2; + } + return f1; +} diff --git a/contrib/file/src/fsmagic.c b/contrib/file/src/fsmagic.c new file mode 100644 index 0000000..ef5492f --- /dev/null +++ b/contrib/file/src/fsmagic.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * fsmagic - magic based on filesystem info - directory, special files, etc. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: fsmagic.c,v 1.73 2014/05/14 23:15:42 christos Exp $") +#endif /* lint */ + +#include "magic.h" +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +/* Since major is a function on SVR4, we cannot use `ifndef major'. */ +#ifdef MAJOR_IN_MKDEV +# include <sys/mkdev.h> +# define HAVE_MAJOR +#endif +#ifdef MAJOR_IN_SYSMACROS +# include <sys/sysmacros.h> +# define HAVE_MAJOR +#endif +#ifdef major /* Might be defined in sys/types.h. */ +# define HAVE_MAJOR +#endif +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#endif + +#ifndef HAVE_MAJOR +# define major(dev) (((dev) >> 8) & 0xff) +# define minor(dev) ((dev) & 0xff) +#endif +#undef HAVE_MAJOR +#ifdef S_IFLNK +private int +bad_link(struct magic_set *ms, int err, char *buf) +{ + int mime = ms->flags & MAGIC_MIME; + if ((mime & MAGIC_MIME_TYPE) && + file_printf(ms, "inode/symlink") + == -1) + return -1; + else if (!mime) { + if (ms->flags & MAGIC_ERROR) { + file_error(ms, err, + "broken symbolic link to `%s'", buf); + return -1; + } + if (file_printf(ms, "broken symbolic link to `%s'", buf) == -1) + return -1; + } + return 1; +} +#endif +private int +handle_mime(struct magic_set *ms, int mime, const char *str) +{ + if ((mime & MAGIC_MIME_TYPE)) { + if (file_printf(ms, "inode/%s", str) == -1) + return -1; + if ((mime & MAGIC_MIME_ENCODING) && file_printf(ms, + "; charset=") == -1) + return -1; + } + if ((mime & MAGIC_MIME_ENCODING) && file_printf(ms, "binary") == -1) + return -1; + return 0; +} + +protected int +file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) +{ + int ret, did = 0; + int mime = ms->flags & MAGIC_MIME; +#ifdef S_IFLNK + char buf[BUFSIZ+4]; + ssize_t nch; + struct stat tstatbuf; +#endif + + if (ms->flags & MAGIC_APPLE) + return 0; + if (fn == NULL) + return 0; + +#define COMMA (did++ ? ", " : "") + /* + * Fstat is cheaper but fails for files you don't have read perms on. + * On 4.2BSD and similar systems, use lstat() to identify symlinks. + */ +#ifdef S_IFLNK + if ((ms->flags & MAGIC_SYMLINK) == 0) + ret = lstat(fn, sb); + else +#endif + ret = stat(fn, sb); /* don't merge into if; see "ret =" above */ + +#ifdef WIN32 + { + HANDLE hFile = CreateFile(fn, 0, FILE_SHARE_DELETE | + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, + NULL); + if (hFile != INVALID_HANDLE_VALUE) { + /* + * Stat failed, but we can still open it - assume it's + * a block device, if nothing else. + */ + if (ret) { + sb->st_mode = S_IFBLK; + ret = 0; + } + switch (GetFileType(hFile)) { + case FILE_TYPE_CHAR: + sb->st_mode |= S_IFCHR; + sb->st_mode &= ~S_IFREG; + break; + case FILE_TYPE_PIPE: + sb->st_mode |= S_IFIFO; + sb->st_mode &= ~S_IFREG; + break; + } + CloseHandle(hFile); + } + } +#endif + + if (ret) { + if (ms->flags & MAGIC_ERROR) { + file_error(ms, errno, "cannot stat `%s'", fn); + return -1; + } + if (file_printf(ms, "cannot open `%s' (%s)", + fn, strerror(errno)) == -1) + return -1; + return 0; + } + + ret = 1; + if (!mime) { +#ifdef S_ISUID + if (sb->st_mode & S_ISUID) + if (file_printf(ms, "%ssetuid", COMMA) == -1) + return -1; +#endif +#ifdef S_ISGID + if (sb->st_mode & S_ISGID) + if (file_printf(ms, "%ssetgid", COMMA) == -1) + return -1; +#endif +#ifdef S_ISVTX + if (sb->st_mode & S_ISVTX) + if (file_printf(ms, "%ssticky", COMMA) == -1) + return -1; +#endif + } + + switch (sb->st_mode & S_IFMT) { + case S_IFDIR: + if (mime) { + if (handle_mime(ms, mime, "directory") == -1) + return -1; + } else if (file_printf(ms, "%sdirectory", COMMA) == -1) + return -1; + break; +#ifdef S_IFCHR + case S_IFCHR: + /* + * If -s has been specified, treat character special files + * like ordinary files. Otherwise, just report that they + * are block special files and go on to the next file. + */ + if ((ms->flags & MAGIC_DEVICES) != 0) { + ret = 0; + break; + } + if (mime) { + if (handle_mime(ms, mime, "chardevice") == -1) + return -1; + } else { +#ifdef HAVE_STRUCT_STAT_ST_RDEV +# ifdef dv_unit + if (file_printf(ms, "%scharacter special (%d/%d/%d)", + COMMA, major(sb->st_rdev), dv_unit(sb->st_rdev), + dv_subunit(sb->st_rdev)) == -1) + return -1; +# else + if (file_printf(ms, "%scharacter special (%ld/%ld)", + COMMA, (long)major(sb->st_rdev), + (long)minor(sb->st_rdev)) == -1) + return -1; +# endif +#else + if (file_printf(ms, "%scharacter special", COMMA) == -1) + return -1; +#endif + } + break; +#endif +#ifdef S_IFBLK + case S_IFBLK: + /* + * If -s has been specified, treat block special files + * like ordinary files. Otherwise, just report that they + * are block special files and go on to the next file. + */ + if ((ms->flags & MAGIC_DEVICES) != 0) { + ret = 0; + break; + } + if (mime) { + if (handle_mime(ms, mime, "blockdevice") == -1) + return -1; + } else { +#ifdef HAVE_STRUCT_STAT_ST_RDEV +# ifdef dv_unit + if (file_printf(ms, "%sblock special (%d/%d/%d)", + COMMA, major(sb->st_rdev), dv_unit(sb->st_rdev), + dv_subunit(sb->st_rdev)) == -1) + return -1; +# else + if (file_printf(ms, "%sblock special (%ld/%ld)", + COMMA, (long)major(sb->st_rdev), + (long)minor(sb->st_rdev)) == -1) + return -1; +# endif +#else + if (file_printf(ms, "%sblock special", COMMA) == -1) + return -1; +#endif + } + break; +#endif + /* TODO add code to handle V7 MUX and Blit MUX files */ +#ifdef S_IFIFO + case S_IFIFO: + if((ms->flags & MAGIC_DEVICES) != 0) + break; + if (mime) { + if (handle_mime(ms, mime, "fifo") == -1) + return -1; + } else if (file_printf(ms, "%sfifo (named pipe)", COMMA) == -1) + return -1; + break; +#endif +#ifdef S_IFDOOR + case S_IFDOOR: + if (mime) { + if (handle_mime(ms, mime, "door") == -1) + return -1; + } else if (file_printf(ms, "%sdoor", COMMA) == -1) + return -1; + break; +#endif +#ifdef S_IFLNK + case S_IFLNK: + if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) { + if (ms->flags & MAGIC_ERROR) { + file_error(ms, errno, "unreadable symlink `%s'", + fn); + return -1; + } + if (mime) { + if (handle_mime(ms, mime, "symlink") == -1) + return -1; + } else if (file_printf(ms, + "%sunreadable symlink `%s' (%s)", COMMA, fn, + strerror(errno)) == -1) + return -1; + break; + } + buf[nch] = '\0'; /* readlink(2) does not do this */ + + /* If broken symlink, say so and quit early. */ + if (*buf == '/') { + if (stat(buf, &tstatbuf) < 0) + return bad_link(ms, errno, buf); + } else { + char *tmp; + char buf2[BUFSIZ+BUFSIZ+4]; + + if ((tmp = strrchr(fn, '/')) == NULL) { + tmp = buf; /* in current directory anyway */ + } else { + if (tmp - fn + 1 > BUFSIZ) { + if (ms->flags & MAGIC_ERROR) { + file_error(ms, 0, + "path too long: `%s'", buf); + return -1; + } + if (mime) { + if (handle_mime(ms, mime, + "x-path-too-long") == -1) + return -1; + } else if (file_printf(ms, + "%spath too long: `%s'", COMMA, + fn) == -1) + return -1; + break; + } + /* take dir part */ + (void)strlcpy(buf2, fn, sizeof buf2); + buf2[tmp - fn + 1] = '\0'; + /* plus (rel) link */ + (void)strlcat(buf2, buf, sizeof buf2); + tmp = buf2; + } + if (stat(tmp, &tstatbuf) < 0) + return bad_link(ms, errno, buf); + } + + /* Otherwise, handle it. */ + if ((ms->flags & MAGIC_SYMLINK) != 0) { + const char *p; + ms->flags &= MAGIC_SYMLINK; + p = magic_file(ms, buf); + ms->flags |= MAGIC_SYMLINK; + if (p == NULL) + return -1; + } else { /* just print what it points to */ + if (mime) { + if (handle_mime(ms, mime, "symlink") == -1) + return -1; + } else if (file_printf(ms, "%ssymbolic link to `%s'", + COMMA, buf) == -1) + return -1; + } + break; +#endif +#ifdef S_IFSOCK +#ifndef __COHERENT__ + case S_IFSOCK: + if (mime) { + if (handle_mime(ms, mime, "socket") == -1) + return -1; + } else if (file_printf(ms, "%ssocket", COMMA) == -1) + return -1; + break; +#endif +#endif + case S_IFREG: + /* + * regular file, check next possibility + * + * If stat() tells us the file has zero length, report here that + * the file is empty, so we can skip all the work of opening and + * reading the file. + * But if the -s option has been given, we skip this + * optimization, since on some systems, stat() reports zero + * size for raw disk partitions. (If the block special device + * really has zero length, the fact that it is empty will be + * detected and reported correctly when we read the file.) + */ + if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) { + if (mime) { + if (handle_mime(ms, mime, "x-empty") == -1) + return -1; + } else if (file_printf(ms, "%sempty", COMMA) == -1) + return -1; + break; + } + ret = 0; + break; + + default: + file_error(ms, 0, "invalid mode 0%o", sb->st_mode); + return -1; + /*NOTREACHED*/ + } + + if (!mime && did && ret == 0) { + if (file_printf(ms, " ") == -1) + return -1; + } + return ret; +} diff --git a/contrib/file/src/funcs.c b/contrib/file/src/funcs.c new file mode 100644 index 0000000..3a2f67c --- /dev/null +++ b/contrib/file/src/funcs.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) Christos Zoulas 2003. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: funcs.c,v 1.72 2014/05/14 23:15:42 christos Exp $") +#endif /* lint */ + +#include "magic.h" +#include <assert.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#if defined(HAVE_WCHAR_H) +#include <wchar.h> +#endif +#if defined(HAVE_WCTYPE_H) +#include <wctype.h> +#endif +#if defined(HAVE_LIMITS_H) +#include <limits.h> +#endif +#if defined(HAVE_LOCALE_H) +#include <locale.h> +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)~0) +#endif + +/* + * Like printf, only we append to a buffer. + */ +protected int +file_vprintf(struct magic_set *ms, const char *fmt, va_list ap) +{ + int len; + char *buf, *newstr; + + if (ms->event_flags & EVENT_HAD_ERR) + return 0; + len = vasprintf(&buf, fmt, ap); + if (len < 0) + goto out; + + if (ms->o.buf != NULL) { + len = asprintf(&newstr, "%s%s", ms->o.buf, buf); + free(buf); + if (len < 0) + goto out; + free(ms->o.buf); + buf = newstr; + } + ms->o.buf = buf; + return 0; +out: + file_error(ms, errno, "vasprintf failed"); + return -1; +} + +protected int +file_printf(struct magic_set *ms, const char *fmt, ...) +{ + int rv; + va_list ap; + + va_start(ap, fmt); + rv = file_vprintf(ms, fmt, ap); + va_end(ap); + return rv; +} + +/* + * error - print best error message possible + */ +/*VARARGS*/ +__attribute__((__format__(__printf__, 3, 0))) +private void +file_error_core(struct magic_set *ms, int error, const char *f, va_list va, + size_t lineno) +{ + /* Only the first error is ok */ + if (ms->event_flags & EVENT_HAD_ERR) + return; + if (lineno != 0) { + free(ms->o.buf); + ms->o.buf = NULL; + file_printf(ms, "line %" SIZE_T_FORMAT "u: ", lineno); + } + file_vprintf(ms, f, va); + if (error > 0) + file_printf(ms, " (%s)", strerror(error)); + ms->event_flags |= EVENT_HAD_ERR; + ms->error = error; +} + +/*VARARGS*/ +protected void +file_error(struct magic_set *ms, int error, const char *f, ...) +{ + va_list va; + va_start(va, f); + file_error_core(ms, error, f, va, 0); + va_end(va); +} + +/* + * Print an error with magic line number. + */ +/*VARARGS*/ +protected void +file_magerror(struct magic_set *ms, const char *f, ...) +{ + va_list va; + va_start(va, f); + file_error_core(ms, 0, f, va, ms->line); + va_end(va); +} + +protected void +file_oomem(struct magic_set *ms, size_t len) +{ + file_error(ms, errno, "cannot allocate %" SIZE_T_FORMAT "u bytes", + len); +} + +protected void +file_badseek(struct magic_set *ms) +{ + file_error(ms, errno, "error seeking"); +} + +protected void +file_badread(struct magic_set *ms) +{ + file_error(ms, errno, "error reading"); +} + +#ifndef COMPILE_ONLY +protected int +file_buffer(struct magic_set *ms, int fd, const char *inname __attribute__ ((unused)), + const void *buf, size_t nb) +{ + int m = 0, rv = 0, looks_text = 0; + int mime = ms->flags & MAGIC_MIME; + const unsigned char *ubuf = CAST(const unsigned char *, buf); + unichar *u8buf = NULL; + size_t ulen; + const char *code = NULL; + const char *code_mime = "binary"; + const char *type = "application/octet-stream"; + const char *def = "data"; + const char *ftype = NULL; + + if (nb == 0) { + def = "empty"; + type = "application/x-empty"; + goto simple; + } else if (nb == 1) { + def = "very short file (no magic)"; + goto simple; + } + + if ((ms->flags & MAGIC_NO_CHECK_ENCODING) == 0) { + looks_text = file_encoding(ms, ubuf, nb, &u8buf, &ulen, + &code, &code_mime, &ftype); + } + +#ifdef __EMX__ + if ((ms->flags & MAGIC_NO_CHECK_APPTYPE) == 0 && inname) { + switch (file_os2_apptype(ms, inname, buf, nb)) { + case -1: + return -1; + case 0: + break; + default: + return 1; + } + } +#endif +#if HAVE_FORK + /* try compression stuff */ + if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) == 0) + if ((m = file_zmagic(ms, fd, inname, ubuf, nb)) != 0) { + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "zmagic %d\n", m); + goto done_encoding; + } +#endif + /* Check if we have a tar file */ + if ((ms->flags & MAGIC_NO_CHECK_TAR) == 0) + if ((m = file_is_tar(ms, ubuf, nb)) != 0) { + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "tar %d\n", m); + goto done; + } + + /* Check if we have a CDF file */ + if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) + if ((m = file_trycdf(ms, fd, ubuf, nb)) != 0) { + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "cdf %d\n", m); + goto done; + } + + /* try soft magic tests */ + if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) + if ((m = file_softmagic(ms, ubuf, nb, 0, BINTEST, + looks_text)) != 0) { + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "softmagic %d\n", m); +#ifdef BUILTIN_ELF + if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && m == 1 && + nb > 5 && fd != -1) { + /* + * We matched something in the file, so this + * *might* be an ELF file, and the file is at + * least 5 bytes long, so if it's an ELF file + * it has at least one byte past the ELF magic + * number - try extracting information from the + * ELF headers that cannot easily * be + * extracted with rules in the magic file. + */ + if ((m = file_tryelf(ms, fd, ubuf, nb)) != 0) + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, + "elf %d\n", m); + } +#endif + goto done; + } + + /* try text properties */ + if ((ms->flags & MAGIC_NO_CHECK_TEXT) == 0) { + + if ((m = file_ascmagic(ms, ubuf, nb, looks_text)) != 0) { + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "ascmagic %d\n", m); + goto done; + } + } + +simple: + /* give up */ + m = 1; + if ((!mime || (mime & MAGIC_MIME_TYPE)) && + file_printf(ms, "%s", mime ? type : def) == -1) { + rv = -1; + } + done: + if ((ms->flags & MAGIC_MIME_ENCODING) != 0) { + if (ms->flags & MAGIC_MIME_TYPE) + if (file_printf(ms, "; charset=") == -1) + rv = -1; + if (file_printf(ms, "%s", code_mime) == -1) + rv = -1; + } +#if HAVE_FORK + done_encoding: +#endif + free(u8buf); + if (rv) + return rv; + + return m; +} +#endif + +protected int +file_reset(struct magic_set *ms) +{ + if (ms->mlist[0] == NULL) { + file_error(ms, 0, "no magic files loaded"); + return -1; + } + if (ms->o.buf) { + free(ms->o.buf); + ms->o.buf = NULL; + } + if (ms->o.pbuf) { + free(ms->o.pbuf); + ms->o.pbuf = NULL; + } + ms->event_flags &= ~EVENT_HAD_ERR; + ms->error = -1; + return 0; +} + +#define OCTALIFY(n, o) \ + /*LINTED*/ \ + (void)(*(n)++ = '\\', \ + *(n)++ = (((uint32_t)*(o) >> 6) & 3) + '0', \ + *(n)++ = (((uint32_t)*(o) >> 3) & 7) + '0', \ + *(n)++ = (((uint32_t)*(o) >> 0) & 7) + '0', \ + (o)++) + +protected const char * +file_getbuffer(struct magic_set *ms) +{ + char *pbuf, *op, *np; + size_t psize, len; + + if (ms->event_flags & EVENT_HAD_ERR) + return NULL; + + if (ms->flags & MAGIC_RAW) + return ms->o.buf; + + if (ms->o.buf == NULL) + return NULL; + + /* * 4 is for octal representation, + 1 is for NUL */ + len = strlen(ms->o.buf); + if (len > (SIZE_MAX - 1) / 4) { + file_oomem(ms, len); + return NULL; + } + psize = len * 4 + 1; + if ((pbuf = CAST(char *, realloc(ms->o.pbuf, psize))) == NULL) { + file_oomem(ms, psize); + return NULL; + } + ms->o.pbuf = pbuf; + +#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) + { + mbstate_t state; + wchar_t nextchar; + int mb_conv = 1; + size_t bytesconsumed; + char *eop; + (void)memset(&state, 0, sizeof(mbstate_t)); + + np = ms->o.pbuf; + op = ms->o.buf; + eop = op + len; + + while (op < eop) { + bytesconsumed = mbrtowc(&nextchar, op, + (size_t)(eop - op), &state); + if (bytesconsumed == (size_t)(-1) || + bytesconsumed == (size_t)(-2)) { + mb_conv = 0; + break; + } + + if (iswprint(nextchar)) { + (void)memcpy(np, op, bytesconsumed); + op += bytesconsumed; + np += bytesconsumed; + } else { + while (bytesconsumed-- > 0) + OCTALIFY(np, op); + } + } + *np = '\0'; + + /* Parsing succeeded as a multi-byte sequence */ + if (mb_conv != 0) + return ms->o.pbuf; + } +#endif + + for (np = ms->o.pbuf, op = ms->o.buf; *op;) { + if (isprint((unsigned char)*op)) { + *np++ = *op++; + } else { + OCTALIFY(np, op); + } + } + *np = '\0'; + return ms->o.pbuf; +} + +protected int +file_check_mem(struct magic_set *ms, unsigned int level) +{ + size_t len; + + if (level >= ms->c.len) { + len = (ms->c.len += 20) * sizeof(*ms->c.li); + ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ? + malloc(len) : + realloc(ms->c.li, len)); + if (ms->c.li == NULL) { + file_oomem(ms, len); + return -1; + } + } + ms->c.li[level].got_match = 0; +#ifdef ENABLE_CONDITIONALS + ms->c.li[level].last_match = 0; + ms->c.li[level].last_cond = COND_NONE; +#endif /* ENABLE_CONDITIONALS */ + return 0; +} + +protected size_t +file_printedlen(const struct magic_set *ms) +{ + return ms->o.buf == NULL ? 0 : strlen(ms->o.buf); +} + +protected int +file_replace(struct magic_set *ms, const char *pat, const char *rep) +{ + file_regex_t rx; + int rc, rv = -1; + + rc = file_regcomp(&rx, pat, REG_EXTENDED); + if (rc) { + file_regerror(&rx, rc, ms); + } else { + regmatch_t rm; + int nm = 0; + while (file_regexec(&rx, ms->o.buf, 1, &rm, 0) == 0) { + ms->o.buf[rm.rm_so] = '\0'; + if (file_printf(ms, "%s%s", rep, + rm.rm_eo != 0 ? ms->o.buf + rm.rm_eo : "") == -1) + goto out; + nm++; + } + rv = nm; + } +out: + file_regfree(&rx); + return rv; +} + +protected int +file_regcomp(file_regex_t *rx, const char *pat, int flags) +{ + rx->old_lc_ctype = setlocale(LC_CTYPE, NULL); + assert(rx->old_lc_ctype != NULL); + rx->old_lc_ctype = strdup(rx->old_lc_ctype); + assert(rx->old_lc_ctype != NULL); + rx->pat = pat; + + (void)setlocale(LC_CTYPE, "C"); + return rx->rc = regcomp(&rx->rx, pat, flags); +} + +protected int +file_regexec(file_regex_t *rx, const char *str, size_t nmatch, + regmatch_t* pmatch, int eflags) +{ + assert(rx->rc == 0); + return regexec(&rx->rx, str, nmatch, pmatch, eflags); +} + +protected void +file_regfree(file_regex_t *rx) +{ + if (rx->rc == 0) + regfree(&rx->rx); + (void)setlocale(LC_CTYPE, rx->old_lc_ctype); + free(rx->old_lc_ctype); +} + +protected void +file_regerror(file_regex_t *rx, int rc, struct magic_set *ms) +{ + char errmsg[512]; + + (void)regerror(rc, &rx->rx, errmsg, sizeof(errmsg)); + file_magerror(ms, "regex error %d for `%s', (%s)", rc, rx->pat, + errmsg); +} diff --git a/contrib/file/src/getline.c b/contrib/file/src/getline.c new file mode 100644 index 0000000..99cd150 --- /dev/null +++ b/contrib/file/src/getline.c @@ -0,0 +1,100 @@ +/* $NetBSD: fgetln.c,v 1.9 2008/04/29 06:53:03 martin Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "file.h" +#if !HAVE_GETLINE +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +public ssize_t +getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) +{ + char *ptr, *eptr; + + + if (*buf == NULL || *bufsiz == 0) { + *bufsiz = BUFSIZ; + if ((*buf = malloc(*bufsiz)) == NULL) + return -1; + } + + for (ptr = *buf, eptr = *buf + *bufsiz;;) { + int c = fgetc(fp); + if (c == -1) { + if (feof(fp)) + return ptr == *buf ? -1 : ptr - *buf; + else + return -1; + } + *ptr++ = c; + if (c == delimiter) { + *ptr = '\0'; + return ptr - *buf; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbufsiz = *bufsiz * 2; + ssize_t d = ptr - *buf; + if ((nbuf = realloc(*buf, nbufsiz)) == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + eptr = nbuf + nbufsiz; + ptr = nbuf + d; + } + } +} + +ssize_t +getline(char **buf, size_t *bufsiz, FILE *fp) +{ + return getdelim(buf, bufsiz, '\n', fp); +} + +#endif + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + char *p = NULL; + ssize_t len; + size_t n = 0; + + while ((len = getline(&p, &n, stdin)) != -1) + (void)printf("%zd %s", len, p); + free(p); + return 0; +} +#endif diff --git a/contrib/file/src/getopt_long.c b/contrib/file/src/getopt_long.c new file mode 100644 index 0000000..d357dc3 --- /dev/null +++ b/contrib/file/src/getopt_long.c @@ -0,0 +1,498 @@ +/* $NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: getopt_long.c,v 1.6 2009/02/13 18:48:05 christos Exp $") +#endif /* lint */ + +#include <assert.h> +#ifdef HAVE_ERR_H +#include <err.h> +#else +#define warnx printf +#endif +#include <errno.h> +#if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION) +#include <getopt.h> +#else +#include "mygetopt.h" +#endif +#include <stdlib.h> +#include <string.h> + +#define REPLACE_GETOPT + +#ifndef _DIAGASSERT +#define _DIAGASSERT assert +#endif + +#ifdef REPLACE_GETOPT +#ifdef __weak_alias +__weak_alias(getopt,_getopt) +#endif +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#elif HAVE_NBTOOL_CONFIG_H && !HAVE_DECL_OPTRESET +static int optreset; +#endif + +#ifdef __weak_alias +__weak_alias(getopt_long,_getopt_long) +#endif + +#define IGNORE_FIRST (*options == '-' || *options == '+') +#define PRINT_ERROR ((opterr) && ((*options != ':') \ + || (IGNORE_FIRST && options[1] != ':'))) +#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL) +#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST) +/* XXX: GNU ignores PC if *options == '-' */ +#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-') + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((IGNORE_FIRST && options[1] == ':') \ + || (*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char **, const char *); +static int gcd(int, int); +static void permute_args(int, int, int, char **); + +static const char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(a, b) + int a; + int b; +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return b; +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(panonopt_start, panonopt_end, opt_end, nargv) + int panonopt_start; + int panonopt_end; + int opt_end; + char **nargv; +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + _DIAGASSERT(nargv != NULL); + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + nargv[pos] = nargv[cstart]; + nargv[cstart] = swap; + } + } +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + * Returns -2 if -- is found (can be long option or end of options marker). + */ +static int +getopt_internal(nargc, nargv, options) + int nargc; + char **nargv; + const char *options; +{ + char *oli; /* option letter list index */ + int optchar; + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + + optarg = NULL; + + /* + * XXX Some programs (like rsyncd) expect to be able to + * XXX re-initialize optind to 0 and have getopt_long(3) + * XXX properly function again. Work around this braindamage. + */ + if (optind == 0) + optind = 1; + + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return -1; + } + if ((*(place = nargv[optind]) != '-') + || (place[1] == '\0')) { /* found non-option */ + place = EMSG; + if (IN_ORDER) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return INORDER; + } + if (!PERMUTE) { + /* + * if no permutation wanted, stop parsing + * at first non-option + */ + return -1; + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + if (place[1] && *++place == '-') { /* found "--" */ + place++; + return -2; + } + } + if ((optchar = (int)*place++) == (int)':' || + (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) { + /* option letter unknown or ':' */ + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return BADCH; + } + if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ + /* XXX: what if no long options provided (called by getopt)? */ + if (*place) + return -2; + + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return BADARG; + } else /* white space */ + place = nargv[optind]; + /* + * Handle -W arg the same as --arg (which causes getopt to + * stop parsing). + */ + return -2; + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = (char *)place; + /* XXX: disable test for :: if PC? (GNU doesn't) */ + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return BADARG; + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return optchar; +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the real getopt] + */ +int +getopt(nargc, nargv, options) + int nargc; + char * const *nargv; + const char *options; +{ + int retval; + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + + retval = getopt_internal(nargc, (char **)nargv, options); + if (retval == -2) { + ++optind; + /* + * We found an option (--), so if we skipped non-options, + * we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, optind, + (char **)nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + retval = -1; + } + return retval; +} +#endif + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(nargc, nargv, options, long_options, idx) + int nargc; + char * const *nargv; + const char *options; + const struct option *long_options; + int *idx; +{ + int retval; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + _DIAGASSERT(long_options != NULL); + /* idx may be NULL */ + + retval = getopt_internal(nargc, (char **)nargv, options); + if (retval == -2) { + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + + current_argv = (char *)place; + match = -1; + ambiguous = 0; + + optind++; + place = EMSG; + + if (*current_argv == '\0') { /* found "--" */ + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, (char **)nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return -1; + } + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == + (unsigned)current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return BADCH; + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of + * flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return BADARG; + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use + * next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' + * indicates no error should be generated + */ + if (PRINT_ERROR) + warnx(recargstring, current_argv); + /* + * XXX: GNU sets optopt to val regardless + * of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return BADARG; + } + } else { /* unknown option */ + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return BADCH; + } + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + retval = 0; + } else + retval = long_options[match].val; + if (idx) + *idx = match; + } + return retval; +#undef IDENTICAL_INTERPRETATION +} diff --git a/contrib/file/src/is_tar.c b/contrib/file/src/is_tar.c new file mode 100644 index 0000000..876c631 --- /dev/null +++ b/contrib/file/src/is_tar.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * is_tar() -- figure out whether file is a tar archive. + * + * Stolen (by the author!) from the public domain tar program: + * Public Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu). + * + * @(#)list.c 1.18 9/23/86 Public Domain - gnu + * + * Comments changed and some code/comments reformatted + * for file command by Ian Darwin. + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: is_tar.c,v 1.37 2010/11/30 14:58:53 rrt Exp $") +#endif + +#include "magic.h" +#include <string.h> +#include <ctype.h> +#include "tar.h" + +#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') ) + +private int is_tar(const unsigned char *, size_t); +private int from_oct(int, const char *); /* Decode octal number */ + +static const char tartype[][32] = { + "tar archive", + "POSIX tar archive", + "POSIX tar archive (GNU)", +}; + +protected int +file_is_tar(struct magic_set *ms, const unsigned char *buf, size_t nbytes) +{ + /* + * Do the tar test first, because if the first file in the tar + * archive starts with a dot, we can confuse it with an nroff file. + */ + int tar; + int mime = ms->flags & MAGIC_MIME; + + if ((ms->flags & MAGIC_APPLE) != 0) + return 0; + + tar = is_tar(buf, nbytes); + if (tar < 1 || tar > 3) + return 0; + + if (file_printf(ms, "%s", mime ? "application/x-tar" : + tartype[tar - 1]) == -1) + return -1; + return 1; +} + +/* + * Return + * 0 if the checksum is bad (i.e., probably not a tar archive), + * 1 for old UNIX tar file, + * 2 for Unix Std (POSIX) tar file, + * 3 for GNU tar file. + */ +private int +is_tar(const unsigned char *buf, size_t nbytes) +{ + const union record *header = (const union record *)(const void *)buf; + int i; + int sum, recsum; + const unsigned char *p; + + if (nbytes < sizeof(union record)) + return 0; + + recsum = from_oct(8, header->header.chksum); + + sum = 0; + p = header->charptr; + for (i = sizeof(union record); --i >= 0;) + sum += *p++; + + /* Adjust checksum to count the "chksum" field as blanks. */ + for (i = sizeof(header->header.chksum); --i >= 0;) + sum -= header->header.chksum[i]; + sum += ' ' * sizeof header->header.chksum; + + if (sum != recsum) + return 0; /* Not a tar archive */ + + if (strcmp(header->header.magic, GNUTMAGIC) == 0) + return 3; /* GNU Unix Standard tar archive */ + if (strcmp(header->header.magic, TMAGIC) == 0) + return 2; /* Unix Standard tar archive */ + + return 1; /* Old fashioned tar archive */ +} + + +/* + * Quick and dirty octal conversion. + * + * Result is -1 if the field is invalid (all blank, or non-octal). + */ +private int +from_oct(int digs, const char *where) +{ + int value; + + while (isspace((unsigned char)*where)) { /* Skip spaces */ + where++; + if (--digs <= 0) + return -1; /* All blank field */ + } + value = 0; + while (digs > 0 && isodigit(*where)) { /* Scan til non-octal */ + value = (value << 3) | (*where++ - '0'); + --digs; + } + + if (digs > 0 && *where && !isspace((unsigned char)*where)) + return -1; /* Ended on non-(space/NUL) */ + + return value; +} diff --git a/contrib/file/src/magic.c b/contrib/file/src/magic.c new file mode 100644 index 0000000..e4bd12b --- /dev/null +++ b/contrib/file/src/magic.c @@ -0,0 +1,524 @@ +/* + * Copyright (c) Christos Zoulas 2003. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef WIN32 +#include <windows.h> +#include <shlwapi.h> +#endif + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: magic.c,v 1.84 2014/05/14 23:15:42 christos Exp $") +#endif /* lint */ + +#include "magic.h" + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#ifdef QUICK +#include <sys/mman.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> /* for PIPE_BUF */ +#endif + +#if defined(HAVE_UTIMES) +# include <sys/time.h> +#elif defined(HAVE_UTIME) +# if defined(HAVE_SYS_UTIME_H) +# include <sys/utime.h> +# elif defined(HAVE_UTIME_H) +# include <utime.h> +# endif +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for read() */ +#endif + +#ifndef PIPE_BUF +/* Get the PIPE_BUF from pathconf */ +#ifdef _PC_PIPE_BUF +#define PIPE_BUF pathconf(".", _PC_PIPE_BUF) +#else +#define PIPE_BUF 512 +#endif +#endif + +private void close_and_restore(const struct magic_set *, const char *, int, + const struct stat *); +private int unreadable_info(struct magic_set *, mode_t, const char *); +private const char* get_default_magic(void); +#ifndef COMPILE_ONLY +private const char *file_or_fd(struct magic_set *, const char *, int); +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +private const char * +get_default_magic(void) +{ + static const char hmagic[] = "/.magic/magic.mgc"; + static char *default_magic; + char *home, *hmagicpath; + +#ifndef WIN32 + struct stat st; + + if (default_magic) { + free(default_magic); + default_magic = NULL; + } + if ((home = getenv("HOME")) == NULL) + return MAGIC; + + if (asprintf(&hmagicpath, "%s/.magic.mgc", home) < 0) + return MAGIC; + if (stat(hmagicpath, &st) == -1) { + free(hmagicpath); + if (asprintf(&hmagicpath, "%s/.magic", home) < 0) + return MAGIC; + if (stat(hmagicpath, &st) == -1) + goto out; + if (S_ISDIR(st.st_mode)) { + free(hmagicpath); + if (asprintf(&hmagicpath, "%s/%s", home, hmagic) < 0) + return MAGIC; + if (access(hmagicpath, R_OK) == -1) + goto out; + } + } + + if (asprintf(&default_magic, "%s:%s", hmagicpath, MAGIC) < 0) + goto out; + free(hmagicpath); + return default_magic; +out: + default_magic = NULL; + free(hmagicpath); + return MAGIC; +#else + char *hmagicp; + char *tmppath = NULL; + hmagicpath = NULL; + +#define APPENDPATH() \ + do { \ + if (tmppath && access(tmppath, R_OK) != -1) { \ + if (hmagicpath == NULL) \ + hmagicpath = tmppath; \ + else { \ + if (asprintf(&hmagicp, "%s%c%s", hmagicpath, \ + PATHSEP, tmppath) >= 0) { \ + free(hmagicpath); \ + hmagicpath = hmagicp; \ + } \ + free(tmppath); \ + } \ + tmppath = NULL; \ + } \ + } while (/*CONSTCOND*/0) + + if (default_magic) { + free(default_magic); + default_magic = NULL; + } + + /* First, try to get user-specific magic file */ + if ((home = getenv("LOCALAPPDATA")) == NULL) { + if ((home = getenv("USERPROFILE")) != NULL) + if (asprintf(&tmppath, + "%s/Local Settings/Application Data%s", home, + hmagic) < 0) + tmppath = NULL; + } else { + if (asprintf(&tmppath, "%s%s", home, hmagic) < 0) + tmppath = NULL; + } + + APPENDPATH(); + + /* Second, try to get a magic file from Common Files */ + if ((home = getenv("COMMONPROGRAMFILES")) != NULL) { + if (asprintf(&tmppath, "%s%s", home, hmagic) >= 0) + APPENDPATH(); + } + + /* Third, try to get magic file relative to dll location */ + LPTSTR dllpath = malloc(sizeof(*dllpath) * (MAX_PATH + 1)); + dllpath[MAX_PATH] = 0; /* just in case long path gets truncated and not null terminated */ + if (GetModuleFileNameA(NULL, dllpath, MAX_PATH)){ + PathRemoveFileSpecA(dllpath); + if (strlen(dllpath) > 3 && + stricmp(&dllpath[strlen(dllpath) - 3], "bin") == 0) { + if (asprintf(&tmppath, + "%s/../share/misc/magic.mgc", dllpath) >= 0) + APPENDPATH(); + } else { + if (asprintf(&tmppath, + "%s/share/misc/magic.mgc", dllpath) >= 0) + APPENDPATH(); + else if (asprintf(&tmppath, + "%s/magic.mgc", dllpath) >= 0) + APPENDPATH(); + } + } + + /* Don't put MAGIC constant - it likely points to a file within MSys + tree */ + default_magic = hmagicpath; + return default_magic; +#endif +} + +public const char * +magic_getpath(const char *magicfile, int action) +{ + if (magicfile != NULL) + return magicfile; + + magicfile = getenv("MAGIC"); + if (magicfile != NULL) + return magicfile; + + return action == FILE_LOAD ? get_default_magic() : MAGIC; +} + +public struct magic_set * +magic_open(int flags) +{ + return file_ms_alloc(flags); +} + +private int +unreadable_info(struct magic_set *ms, mode_t md, const char *file) +{ + if (file) { + /* We cannot open it, but we were able to stat it. */ + if (access(file, W_OK) == 0) + if (file_printf(ms, "writable, ") == -1) + return -1; + if (access(file, X_OK) == 0) + if (file_printf(ms, "executable, ") == -1) + return -1; + } + if (S_ISREG(md)) + if (file_printf(ms, "regular file, ") == -1) + return -1; + if (file_printf(ms, "no read permission") == -1) + return -1; + return 0; +} + +public void +magic_close(struct magic_set *ms) +{ + if (ms == NULL) + return; + file_ms_free(ms); +} + +/* + * load a magic file + */ +public int +magic_load(struct magic_set *ms, const char *magicfile) +{ + if (ms == NULL) + return -1; + return file_apprentice(ms, magicfile, FILE_LOAD); +} + +public int +magic_compile(struct magic_set *ms, const char *magicfile) +{ + if (ms == NULL) + return -1; + return file_apprentice(ms, magicfile, FILE_COMPILE); +} + +public int +magic_check(struct magic_set *ms, const char *magicfile) +{ + if (ms == NULL) + return -1; + return file_apprentice(ms, magicfile, FILE_CHECK); +} + +public int +magic_list(struct magic_set *ms, const char *magicfile) +{ + if (ms == NULL) + return -1; + return file_apprentice(ms, magicfile, FILE_LIST); +} + +private void +close_and_restore(const struct magic_set *ms, const char *name, int fd, + const struct stat *sb) +{ + if (fd == STDIN_FILENO || name == NULL) + return; + (void) close(fd); + + if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) { + /* + * Try to restore access, modification times if read it. + * This is really *bad* because it will modify the status + * time of the file... And of course this will affect + * backup programs + */ +#ifdef HAVE_UTIMES + struct timeval utsbuf[2]; + (void)memset(utsbuf, 0, sizeof(utsbuf)); + utsbuf[0].tv_sec = sb->st_atime; + utsbuf[1].tv_sec = sb->st_mtime; + + (void) utimes(name, utsbuf); /* don't care if loses */ +#elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H) + struct utimbuf utbuf; + + (void)memset(&utbuf, 0, sizeof(utbuf)); + utbuf.actime = sb->st_atime; + utbuf.modtime = sb->st_mtime; + (void) utime(name, &utbuf); /* don't care if loses */ +#endif + } +} + +#ifndef COMPILE_ONLY + +/* + * find type of descriptor + */ +public const char * +magic_descriptor(struct magic_set *ms, int fd) +{ + if (ms == NULL) + return NULL; + return file_or_fd(ms, NULL, fd); +} + +/* + * find type of named file + */ +public const char * +magic_file(struct magic_set *ms, const char *inname) +{ + if (ms == NULL) + return NULL; + return file_or_fd(ms, inname, STDIN_FILENO); +} + +private const char * +file_or_fd(struct magic_set *ms, const char *inname, int fd) +{ + int rv = -1; + unsigned char *buf; + struct stat sb; + ssize_t nbytes = 0; /* number of bytes read from a datafile */ + int ispipe = 0; + off_t pos = (off_t)-1; + + if (file_reset(ms) == -1) + goto out; + + /* + * one extra for terminating '\0', and + * some overlapping space for matches near EOF + */ +#define SLOP (1 + sizeof(union VALUETYPE)) + if ((buf = CAST(unsigned char *, malloc(HOWMANY + SLOP))) == NULL) + return NULL; + + switch (file_fsmagic(ms, inname, &sb)) { + case -1: /* error */ + goto done; + case 0: /* nothing found */ + break; + default: /* matched it and printed type */ + rv = 0; + goto done; + } + +#ifdef WIN32 + /* Place stdin in binary mode, so EOF (Ctrl+Z) doesn't stop early. */ + if (fd == STDIN_FILENO) + _setmode(STDIN_FILENO, O_BINARY); +#endif + + if (inname == NULL) { + if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) + ispipe = 1; + else + pos = lseek(fd, (off_t)0, SEEK_CUR); + } else { + int flags = O_RDONLY|O_BINARY; + int okstat = stat(inname, &sb) == 0; + + if (okstat && S_ISFIFO(sb.st_mode)) { +#ifdef O_NONBLOCK + flags |= O_NONBLOCK; +#endif + ispipe = 1; + } + + errno = 0; + if ((fd = open(inname, flags)) < 0) { +#ifdef WIN32 + /* + * Can't stat, can't open. It may have been opened in + * fsmagic, so if the user doesn't have read permission, + * allow it to say so; otherwise an error was probably + * displayed in fsmagic. + */ + if (!okstat && errno == EACCES) { + sb.st_mode = S_IFBLK; + okstat = 1; + } +#endif + if (okstat && + unreadable_info(ms, sb.st_mode, inname) == -1) + goto done; + rv = 0; + goto done; + } +#ifdef O_NONBLOCK + if ((flags = fcntl(fd, F_GETFL)) != -1) { + flags &= ~O_NONBLOCK; + (void)fcntl(fd, F_SETFL, flags); + } +#endif + } + + /* + * try looking at the first HOWMANY bytes + */ + if (ispipe) { + ssize_t r = 0; + + while ((r = sread(fd, (void *)&buf[nbytes], + (size_t)(HOWMANY - nbytes), 1)) > 0) { + nbytes += r; + if (r < PIPE_BUF) break; + } + + if (nbytes == 0) { + /* We can not read it, but we were able to stat it. */ + if (unreadable_info(ms, sb.st_mode, inname) == -1) + goto done; + rv = 0; + goto done; + } + + } else { + /* Windows refuses to read from a big console buffer. */ + size_t howmany = +#if defined(WIN32) && HOWMANY > 8 * 1024 + _isatty(fd) ? 8 * 1024 : +#endif + HOWMANY; + if ((nbytes = read(fd, (char *)buf, howmany)) == -1) { + if (inname == NULL && fd != STDIN_FILENO) + file_error(ms, errno, "cannot read fd %d", fd); + else + file_error(ms, errno, "cannot read `%s'", + inname == NULL ? "/dev/stdin" : inname); + goto done; + } + } + + (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */ + if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1) + goto done; + rv = 0; +done: + free(buf); + if (pos != (off_t)-1) + (void)lseek(fd, pos, SEEK_SET); + close_and_restore(ms, inname, fd, &sb); +out: + return rv == 0 ? file_getbuffer(ms) : NULL; +} + + +public const char * +magic_buffer(struct magic_set *ms, const void *buf, size_t nb) +{ + if (ms == NULL) + return NULL; + if (file_reset(ms) == -1) + return NULL; + /* + * The main work is done here! + * We have the file name and/or the data buffer to be identified. + */ + if (file_buffer(ms, -1, NULL, buf, nb) == -1) { + return NULL; + } + return file_getbuffer(ms); +} +#endif + +public const char * +magic_error(struct magic_set *ms) +{ + if (ms == NULL) + return "Magic database is not open"; + return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL; +} + +public int +magic_errno(struct magic_set *ms) +{ + if (ms == NULL) + return EINVAL; + return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0; +} + +public int +magic_setflags(struct magic_set *ms, int flags) +{ + if (ms == NULL) + return -1; +#if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES) + if (flags & MAGIC_PRESERVE_ATIME) + return -1; +#endif + ms->flags = flags; + return 0; +} + +public int +magic_version(void) +{ + return MAGIC_VERSION; +} diff --git a/contrib/file/src/magic.h b/contrib/file/src/magic.h new file mode 100644 index 0000000..38e59d7 --- /dev/null +++ b/contrib/file/src/magic.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) Christos Zoulas 2003. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _MAGIC_H +#define _MAGIC_H + +#include <sys/types.h> + +#define MAGIC_NONE 0x000000 /* No flags */ +#define MAGIC_DEBUG 0x000001 /* Turn on debugging */ +#define MAGIC_SYMLINK 0x000002 /* Follow symlinks */ +#define MAGIC_COMPRESS 0x000004 /* Check inside compressed files */ +#define MAGIC_DEVICES 0x000008 /* Look at the contents of devices */ +#define MAGIC_MIME_TYPE 0x000010 /* Return the MIME type */ +#define MAGIC_CONTINUE 0x000020 /* Return all matches */ +#define MAGIC_CHECK 0x000040 /* Print warnings to stderr */ +#define MAGIC_PRESERVE_ATIME 0x000080 /* Restore access time on exit */ +#define MAGIC_RAW 0x000100 /* Don't translate unprintable chars */ +#define MAGIC_ERROR 0x000200 /* Handle ENOENT etc as real errors */ +#define MAGIC_MIME_ENCODING 0x000400 /* Return the MIME encoding */ +#define MAGIC_MIME (MAGIC_MIME_TYPE|MAGIC_MIME_ENCODING) +#define MAGIC_APPLE 0x000800 /* Return the Apple creator and type */ + +#define MAGIC_NO_CHECK_COMPRESS 0x001000 /* Don't check for compressed files */ +#define MAGIC_NO_CHECK_TAR 0x002000 /* Don't check for tar files */ +#define MAGIC_NO_CHECK_SOFT 0x004000 /* Don't check magic entries */ +#define MAGIC_NO_CHECK_APPTYPE 0x008000 /* Don't check application type */ +#define MAGIC_NO_CHECK_ELF 0x010000 /* Don't check for elf details */ +#define MAGIC_NO_CHECK_TEXT 0x020000 /* Don't check for text files */ +#define MAGIC_NO_CHECK_CDF 0x040000 /* Don't check for cdf files */ +#define MAGIC_NO_CHECK_TOKENS 0x100000 /* Don't check tokens */ +#define MAGIC_NO_CHECK_ENCODING 0x200000 /* Don't check text encodings */ + +/* No built-in tests; only consult the magic file */ +#define MAGIC_NO_CHECK_BUILTIN ( \ + MAGIC_NO_CHECK_COMPRESS | \ + MAGIC_NO_CHECK_TAR | \ +/* MAGIC_NO_CHECK_SOFT | */ \ + MAGIC_NO_CHECK_APPTYPE | \ + MAGIC_NO_CHECK_ELF | \ + MAGIC_NO_CHECK_TEXT | \ + MAGIC_NO_CHECK_CDF | \ + MAGIC_NO_CHECK_TOKENS | \ + MAGIC_NO_CHECK_ENCODING | \ + 0 \ +) + +/* Defined for backwards compatibility (renamed) */ +#define MAGIC_NO_CHECK_ASCII MAGIC_NO_CHECK_TEXT + +/* Defined for backwards compatibility; do nothing */ +#define MAGIC_NO_CHECK_FORTRAN 0x000000 /* Don't check ascii/fortran */ +#define MAGIC_NO_CHECK_TROFF 0x000000 /* Don't check ascii/troff */ + +#define MAGIC_VERSION 518 /* This implementation */ + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct magic_set *magic_t; +magic_t magic_open(int); +void magic_close(magic_t); + +const char *magic_getpath(const char *, int); +const char *magic_file(magic_t, const char *); +const char *magic_descriptor(magic_t, int); +const char *magic_buffer(magic_t, const void *, size_t); + +const char *magic_error(magic_t); +int magic_setflags(magic_t, int); + +int magic_version(void); +int magic_load(magic_t, const char *); +int magic_compile(magic_t, const char *); +int magic_check(magic_t, const char *); +int magic_list(magic_t, const char *); +int magic_errno(magic_t); + +#ifdef __cplusplus +}; +#endif + +#endif /* _MAGIC_H */ diff --git a/contrib/file/src/magic.h.in b/contrib/file/src/magic.h.in new file mode 100644 index 0000000..86fc41b --- /dev/null +++ b/contrib/file/src/magic.h.in @@ -0,0 +1,108 @@ +/* + * Copyright (c) Christos Zoulas 2003. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _MAGIC_H +#define _MAGIC_H + +#include <sys/types.h> + +#define MAGIC_NONE 0x000000 /* No flags */ +#define MAGIC_DEBUG 0x000001 /* Turn on debugging */ +#define MAGIC_SYMLINK 0x000002 /* Follow symlinks */ +#define MAGIC_COMPRESS 0x000004 /* Check inside compressed files */ +#define MAGIC_DEVICES 0x000008 /* Look at the contents of devices */ +#define MAGIC_MIME_TYPE 0x000010 /* Return the MIME type */ +#define MAGIC_CONTINUE 0x000020 /* Return all matches */ +#define MAGIC_CHECK 0x000040 /* Print warnings to stderr */ +#define MAGIC_PRESERVE_ATIME 0x000080 /* Restore access time on exit */ +#define MAGIC_RAW 0x000100 /* Don't translate unprintable chars */ +#define MAGIC_ERROR 0x000200 /* Handle ENOENT etc as real errors */ +#define MAGIC_MIME_ENCODING 0x000400 /* Return the MIME encoding */ +#define MAGIC_MIME (MAGIC_MIME_TYPE|MAGIC_MIME_ENCODING) +#define MAGIC_APPLE 0x000800 /* Return the Apple creator and type */ + +#define MAGIC_NO_CHECK_COMPRESS 0x001000 /* Don't check for compressed files */ +#define MAGIC_NO_CHECK_TAR 0x002000 /* Don't check for tar files */ +#define MAGIC_NO_CHECK_SOFT 0x004000 /* Don't check magic entries */ +#define MAGIC_NO_CHECK_APPTYPE 0x008000 /* Don't check application type */ +#define MAGIC_NO_CHECK_ELF 0x010000 /* Don't check for elf details */ +#define MAGIC_NO_CHECK_TEXT 0x020000 /* Don't check for text files */ +#define MAGIC_NO_CHECK_CDF 0x040000 /* Don't check for cdf files */ +#define MAGIC_NO_CHECK_TOKENS 0x100000 /* Don't check tokens */ +#define MAGIC_NO_CHECK_ENCODING 0x200000 /* Don't check text encodings */ + +/* No built-in tests; only consult the magic file */ +#define MAGIC_NO_CHECK_BUILTIN ( \ + MAGIC_NO_CHECK_COMPRESS | \ + MAGIC_NO_CHECK_TAR | \ +/* MAGIC_NO_CHECK_SOFT | */ \ + MAGIC_NO_CHECK_APPTYPE | \ + MAGIC_NO_CHECK_ELF | \ + MAGIC_NO_CHECK_TEXT | \ + MAGIC_NO_CHECK_CDF | \ + MAGIC_NO_CHECK_TOKENS | \ + MAGIC_NO_CHECK_ENCODING | \ + 0 \ +) + +/* Defined for backwards compatibility (renamed) */ +#define MAGIC_NO_CHECK_ASCII MAGIC_NO_CHECK_TEXT + +/* Defined for backwards compatibility; do nothing */ +#define MAGIC_NO_CHECK_FORTRAN 0x000000 /* Don't check ascii/fortran */ +#define MAGIC_NO_CHECK_TROFF 0x000000 /* Don't check ascii/troff */ + +#define MAGIC_VERSION X.YY /* This implementation */ + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct magic_set *magic_t; +magic_t magic_open(int); +void magic_close(magic_t); + +const char *magic_getpath(const char *, int); +const char *magic_file(magic_t, const char *); +const char *magic_descriptor(magic_t, int); +const char *magic_buffer(magic_t, const void *, size_t); + +const char *magic_error(magic_t); +int magic_setflags(magic_t, int); + +int magic_version(void); +int magic_load(magic_t, const char *); +int magic_compile(magic_t, const char *); +int magic_check(magic_t, const char *); +int magic_list(magic_t, const char *); +int magic_errno(magic_t); + +#ifdef __cplusplus +}; +#endif + +#endif /* _MAGIC_H */ diff --git a/contrib/file/src/mygetopt.h b/contrib/file/src/mygetopt.h new file mode 100644 index 0000000..ef87525 --- /dev/null +++ b/contrib/file/src/mygetopt.h @@ -0,0 +1,68 @@ +/* $NetBSD: getopt.h,v 1.8 2007/11/06 19:21:18 christos Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#include <unistd.h> + +/* + * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +int getopt_long(int, char * const *, const char *, + const struct option *, int *); + +#endif /* !_GETOPT_H_ */ diff --git a/contrib/file/src/pread.c b/contrib/file/src/pread.c new file mode 100644 index 0000000..649f609 --- /dev/null +++ b/contrib/file/src/pread.c @@ -0,0 +1,14 @@ +#include "file.h" +#ifndef lint +FILE_RCSID("@(#)$File: pread.c,v 1.2 2013/04/02 16:23:07 christos Exp $") +#endif /* lint */ +#include <fcntl.h> +#include <unistd.h> + +ssize_t +pread(int fd, void *buf, size_t len, off_t off) { + if (lseek(fd, off, SEEK_SET) == (off_t)-1) + return -1; + + return read(fd, buf, len); +} diff --git a/contrib/file/src/print.c b/contrib/file/src/print.c new file mode 100644 index 0000000..fa81798 --- /dev/null +++ b/contrib/file/src/print.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * print.c - debugging printout routines + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: print.c,v 1.76 2013/02/26 18:25:00 christos Exp $") +#endif /* lint */ + +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <time.h> + +#define SZOF(a) (sizeof(a) / sizeof(a[0])) + +#include "cdf.h" + +#ifndef COMPILE_ONLY +protected void +file_mdump(struct magic *m) +{ + static const char optyp[] = { FILE_OPS }; + char tbuf[26]; + + (void) fprintf(stderr, "%u: %.*s %u", m->lineno, + (m->cont_level & 7) + 1, ">>>>>>>>", m->offset); + + if (m->flag & INDIR) { + (void) fprintf(stderr, "(%s,", + /* Note: type is unsigned */ + (m->in_type < file_nnames) ? file_names[m->in_type] : + "*bad in_type*"); + if (m->in_op & FILE_OPINVERSE) + (void) fputc('~', stderr); + (void) fprintf(stderr, "%c%u),", + ((size_t)(m->in_op & FILE_OPS_MASK) < + SZOF(optyp)) ? optyp[m->in_op & FILE_OPS_MASK] : '?', + m->in_offset); + } + (void) fprintf(stderr, " %s%s", (m->flag & UNSIGNED) ? "u" : "", + /* Note: type is unsigned */ + (m->type < file_nnames) ? file_names[m->type] : "*bad type"); + if (m->mask_op & FILE_OPINVERSE) + (void) fputc('~', stderr); + + if (IS_STRING(m->type)) { + if (m->str_flags) { + (void) fputc('/', stderr); + if (m->str_flags & STRING_COMPACT_WHITESPACE) + (void) fputc(CHAR_COMPACT_WHITESPACE, stderr); + if (m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) + (void) fputc(CHAR_COMPACT_OPTIONAL_WHITESPACE, + stderr); + if (m->str_flags & STRING_IGNORE_LOWERCASE) + (void) fputc(CHAR_IGNORE_LOWERCASE, stderr); + if (m->str_flags & STRING_IGNORE_UPPERCASE) + (void) fputc(CHAR_IGNORE_UPPERCASE, stderr); + if (m->str_flags & REGEX_OFFSET_START) + (void) fputc(CHAR_REGEX_OFFSET_START, stderr); + if (m->str_flags & STRING_TEXTTEST) + (void) fputc(CHAR_TEXTTEST, stderr); + if (m->str_flags & STRING_BINTEST) + (void) fputc(CHAR_BINTEST, stderr); + if (m->str_flags & PSTRING_1_BE) + (void) fputc(CHAR_PSTRING_1_BE, stderr); + if (m->str_flags & PSTRING_2_BE) + (void) fputc(CHAR_PSTRING_2_BE, stderr); + if (m->str_flags & PSTRING_2_LE) + (void) fputc(CHAR_PSTRING_2_LE, stderr); + if (m->str_flags & PSTRING_4_BE) + (void) fputc(CHAR_PSTRING_4_BE, stderr); + if (m->str_flags & PSTRING_4_LE) + (void) fputc(CHAR_PSTRING_4_LE, stderr); + if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF) + (void) fputc( + CHAR_PSTRING_LENGTH_INCLUDES_ITSELF, + stderr); + } + if (m->str_range) + (void) fprintf(stderr, "/%u", m->str_range); + } + else { + if ((size_t)(m->mask_op & FILE_OPS_MASK) < SZOF(optyp)) + (void) fputc(optyp[m->mask_op & FILE_OPS_MASK], stderr); + else + (void) fputc('?', stderr); + + if (m->num_mask) { + (void) fprintf(stderr, "%.8llx", + (unsigned long long)m->num_mask); + } + } + (void) fprintf(stderr, ",%c", m->reln); + + if (m->reln != 'x') { + switch (m->type) { + case FILE_BYTE: + case FILE_SHORT: + case FILE_LONG: + case FILE_LESHORT: + case FILE_LELONG: + case FILE_MELONG: + case FILE_BESHORT: + case FILE_BELONG: + case FILE_INDIRECT: + (void) fprintf(stderr, "%d", m->value.l); + break; + case FILE_BEQUAD: + case FILE_LEQUAD: + case FILE_QUAD: + (void) fprintf(stderr, "%" INT64_T_FORMAT "d", + (unsigned long long)m->value.q); + break; + case FILE_PSTRING: + case FILE_STRING: + case FILE_REGEX: + case FILE_BESTRING16: + case FILE_LESTRING16: + case FILE_SEARCH: + file_showstr(stderr, m->value.s, (size_t)m->vallen); + break; + case FILE_DATE: + case FILE_LEDATE: + case FILE_BEDATE: + case FILE_MEDATE: + (void)fprintf(stderr, "%s,", + file_fmttime(m->value.l, FILE_T_LOCAL, tbuf)); + break; + case FILE_LDATE: + case FILE_LELDATE: + case FILE_BELDATE: + case FILE_MELDATE: + (void)fprintf(stderr, "%s,", + file_fmttime(m->value.l, 0, tbuf)); + case FILE_QDATE: + case FILE_LEQDATE: + case FILE_BEQDATE: + (void)fprintf(stderr, "%s,", + file_fmttime(m->value.q, FILE_T_LOCAL, tbuf)); + break; + case FILE_QLDATE: + case FILE_LEQLDATE: + case FILE_BEQLDATE: + (void)fprintf(stderr, "%s,", + file_fmttime(m->value.q, 0, tbuf)); + break; + case FILE_QWDATE: + case FILE_LEQWDATE: + case FILE_BEQWDATE: + (void)fprintf(stderr, "%s,", + file_fmttime(m->value.q, FILE_T_WINDOWS, tbuf)); + break; + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + (void) fprintf(stderr, "%G", m->value.f); + break; + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + (void) fprintf(stderr, "%G", m->value.d); + break; + case FILE_DEFAULT: + /* XXX - do anything here? */ + break; + case FILE_USE: + case FILE_NAME: + (void) fprintf(stderr, "'%s'", m->value.s); + break; + default: + (void) fprintf(stderr, "*bad type %d*", m->type); + break; + } + } + (void) fprintf(stderr, ",\"%s\"]\n", m->desc); +} +#endif + +/*VARARGS*/ +protected void +file_magwarn(struct magic_set *ms, const char *f, ...) +{ + va_list va; + + /* cuz we use stdout for most, stderr here */ + (void) fflush(stdout); + + if (ms->file) + (void) fprintf(stderr, "%s, %lu: ", ms->file, + (unsigned long)ms->line); + (void) fprintf(stderr, "Warning: "); + va_start(va, f); + (void) vfprintf(stderr, f, va); + va_end(va); + (void) fputc('\n', stderr); +} + +protected const char * +file_fmttime(uint64_t v, int flags, char *buf) +{ + char *pp; + time_t t = (time_t)v; + struct tm *tm; + + if (flags & FILE_T_WINDOWS) { + struct timespec ts; + cdf_timestamp_to_timespec(&ts, t); + t = ts.tv_sec; + } + + if (flags & FILE_T_LOCAL) { + pp = ctime_r(&t, buf); + } else { +#ifndef HAVE_DAYLIGHT + private int daylight = 0; +#ifdef HAVE_TM_ISDST + private time_t now = (time_t)0; + + if (now == (time_t)0) { + struct tm *tm1; + (void)time(&now); + tm1 = localtime(&now); + if (tm1 == NULL) + goto out; + daylight = tm1->tm_isdst; + } +#endif /* HAVE_TM_ISDST */ +#endif /* HAVE_DAYLIGHT */ + if (daylight) + t += 3600; + tm = gmtime(&t); + if (tm == NULL) + goto out; + pp = asctime_r(tm, buf); + } + + if (pp == NULL) + goto out; + pp[strcspn(pp, "\n")] = '\0'; + return pp; +out: + return strcpy(buf, "*Invalid time*"); +} diff --git a/contrib/file/src/readcdf.c b/contrib/file/src/readcdf.c new file mode 100644 index 0000000..7ced9ea --- /dev/null +++ b/contrib/file/src/readcdf.c @@ -0,0 +1,473 @@ +/*- + * Copyright (c) 2008 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: readcdf.c,v 1.44 2014/05/14 23:22:48 christos Exp $") +#endif + +#include <assert.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#if defined(HAVE_LOCALE_H) +#include <locale.h> +#endif + +#include "cdf.h" +#include "magic.h" + +#define NOTMIME(ms) (((ms)->flags & MAGIC_MIME) == 0) + +static const struct nv { + const char *pattern; + const char *mime; +} app2mime[] = { + { "Word", "msword", }, + { "Excel", "vnd.ms-excel", }, + { "Powerpoint", "vnd.ms-powerpoint", }, + { "Crystal Reports", "x-rpt", }, + { "Advanced Installer", "vnd.ms-msi", }, + { "InstallShield", "vnd.ms-msi", }, + { "Microsoft Patch Compiler", "vnd.ms-msi", }, + { "NAnt", "vnd.ms-msi", }, + { "Windows Installer", "vnd.ms-msi", }, + { NULL, NULL, }, +}, name2mime[] = { + { "WordDocument", "msword", }, + { "PowerPoint", "vnd.ms-powerpoint", }, + { "DigitalSignature", "vnd.ms-msi", }, + { NULL, NULL, }, +}, name2desc[] = { + { "WordDocument", "Microsoft Office Word",}, + { "PowerPoint", "Microsoft PowerPoint", }, + { "DigitalSignature", "Microsoft Installer", }, + { NULL, NULL, }, +}; + +static const struct cv { + uint64_t clsid[2]; + const char *mime; +} clsid2mime[] = { + { + { 0x00000000000c1084LLU, 0x46000000000000c0LLU }, + "x-msi", + }, + { { 0, 0 }, + NULL, + }, +}, clsid2desc[] = { + { + { 0x00000000000c1084LLU, 0x46000000000000c0LLU }, + "MSI Installer", + }, + { { 0, 0 }, + NULL, + }, +}; + +private const char * +cdf_clsid_to_mime(const uint64_t clsid[2], const struct cv *cv) +{ + size_t i; + for (i = 0; cv[i].mime != NULL; i++) { + if (clsid[0] == cv[i].clsid[0] && clsid[1] == cv[i].clsid[1]) + return cv[i].mime; + } + return NULL; +} + +private const char * +cdf_app_to_mime(const char *vbuf, const struct nv *nv) +{ + size_t i; + const char *rv = NULL; + char *old_lc_ctype; + + old_lc_ctype = setlocale(LC_CTYPE, NULL); + assert(old_lc_ctype != NULL); + old_lc_ctype = strdup(old_lc_ctype); + assert(old_lc_ctype != NULL); + (void)setlocale(LC_CTYPE, "C"); + for (i = 0; nv[i].pattern != NULL; i++) + if (strcasestr(vbuf, nv[i].pattern) != NULL) { + rv = nv[i].mime; + break; + } + (void)setlocale(LC_CTYPE, old_lc_ctype); + free(old_lc_ctype); + return rv; +} + +private int +cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info, + size_t count, const cdf_directory_t *root_storage) +{ + size_t i; + cdf_timestamp_t tp; + struct timespec ts; + char buf[64]; + const char *str = NULL; + const char *s; + int len; + + if (!NOTMIME(ms) && root_storage) + str = cdf_clsid_to_mime(root_storage->d_storage_uuid, + clsid2mime); + + for (i = 0; i < count; i++) { + cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); + switch (info[i].pi_type) { + case CDF_NULL: + break; + case CDF_SIGNED16: + if (NOTMIME(ms) && file_printf(ms, ", %s: %hd", buf, + info[i].pi_s16) == -1) + return -1; + break; + case CDF_SIGNED32: + if (NOTMIME(ms) && file_printf(ms, ", %s: %d", buf, + info[i].pi_s32) == -1) + return -1; + break; + case CDF_UNSIGNED32: + if (NOTMIME(ms) && file_printf(ms, ", %s: %u", buf, + info[i].pi_u32) == -1) + return -1; + break; + case CDF_FLOAT: + if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf, + info[i].pi_f) == -1) + return -1; + break; + case CDF_DOUBLE: + if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf, + info[i].pi_d) == -1) + return -1; + break; + case CDF_LENGTH32_STRING: + case CDF_LENGTH32_WSTRING: + len = info[i].pi_str.s_len; + if (len > 1) { + char vbuf[1024]; + size_t j, k = 1; + + if (info[i].pi_type == CDF_LENGTH32_WSTRING) + k++; + s = info[i].pi_str.s_buf; + for (j = 0; j < sizeof(vbuf) && len--; s += k) { + if (*s == '\0') + break; + if (isprint((unsigned char)*s)) + vbuf[j++] = *s; + } + if (j == sizeof(vbuf)) + --j; + vbuf[j] = '\0'; + if (NOTMIME(ms)) { + if (vbuf[0]) { + if (file_printf(ms, ", %s: %s", + buf, vbuf) == -1) + return -1; + } + } else if (str == NULL && info[i].pi_id == + CDF_PROPERTY_NAME_OF_APPLICATION) { + str = cdf_app_to_mime(vbuf, app2mime); + } + } + break; + case CDF_FILETIME: + tp = info[i].pi_tp; + if (tp != 0) { + char tbuf[64]; + if (tp < 1000000000000000LL) { + cdf_print_elapsed_time(tbuf, + sizeof(tbuf), tp); + if (NOTMIME(ms) && file_printf(ms, + ", %s: %s", buf, tbuf) == -1) + return -1; + } else { + char *c, *ec; + cdf_timestamp_to_timespec(&ts, tp); + c = cdf_ctime(&ts.tv_sec, tbuf); + if (c != NULL && + (ec = strchr(c, '\n')) != NULL) + *ec = '\0'; + + if (NOTMIME(ms) && file_printf(ms, + ", %s: %s", buf, c) == -1) + return -1; + } + } + break; + case CDF_CLIPBOARD: + break; + default: + return -1; + } + } + if (!NOTMIME(ms)) { + if (str == NULL) + return 0; + if (file_printf(ms, "application/%s", str) == -1) + return -1; + } + return 1; +} + +private int +cdf_file_summary_info(struct magic_set *ms, const cdf_header_t *h, + const cdf_stream_t *sst, const cdf_directory_t *root_storage) +{ + cdf_summary_info_header_t si; + cdf_property_info_t *info; + size_t count; + int m; + + if (cdf_unpack_summary_info(sst, h, &si, &info, &count) == -1) + return -1; + + if (NOTMIME(ms)) { + const char *str; + + if (file_printf(ms, "Composite Document File V2 Document") + == -1) + return -1; + + if (file_printf(ms, ", %s Endian", + si.si_byte_order == 0xfffe ? "Little" : "Big") == -1) + return -2; + switch (si.si_os) { + case 2: + if (file_printf(ms, ", Os: Windows, Version %d.%d", + si.si_os_version & 0xff, + (uint32_t)si.si_os_version >> 8) == -1) + return -2; + break; + case 1: + if (file_printf(ms, ", Os: MacOS, Version %d.%d", + (uint32_t)si.si_os_version >> 8, + si.si_os_version & 0xff) == -1) + return -2; + break; + default: + if (file_printf(ms, ", Os %d, Version: %d.%d", si.si_os, + si.si_os_version & 0xff, + (uint32_t)si.si_os_version >> 8) == -1) + return -2; + break; + } + if (root_storage) { + str = cdf_clsid_to_mime(root_storage->d_storage_uuid, + clsid2desc); + if (str) + if (file_printf(ms, ", %s", str) == -1) + return -2; + } + } + + m = cdf_file_property_info(ms, info, count, root_storage); + free(info); + + return m == -1 ? -2 : m; +} + +#ifdef notdef +private char * +format_clsid(char *buf, size_t len, const uint64_t uuid[2]) { + snprintf(buf, len, "%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4" + PRIx64 "-%.12" PRIx64, + (uuid[0] >> 32) & (uint64_t)0x000000000ffffffffLLU, + (uuid[0] >> 16) & (uint64_t)0x0000000000000ffffLLU, + (uuid[0] >> 0) & (uint64_t)0x0000000000000ffffLLU, + (uuid[1] >> 48) & (uint64_t)0x0000000000000ffffLLU, + (uuid[1] >> 0) & (uint64_t)0x0000fffffffffffffLLU); + return buf; +} +#endif + +protected int +file_trycdf(struct magic_set *ms, int fd, const unsigned char *buf, + size_t nbytes) +{ + cdf_info_t info; + cdf_header_t h; + cdf_sat_t sat, ssat; + cdf_stream_t sst, scn; + cdf_dir_t dir; + int i; + const char *expn = ""; + const char *corrupt = "corrupt: "; + + info.i_fd = fd; + info.i_buf = buf; + info.i_len = nbytes; + if (ms->flags & MAGIC_APPLE) + return 0; + if (cdf_read_header(&info, &h) == -1) + return 0; +#ifdef CDF_DEBUG + cdf_dump_header(&h); +#endif + + if ((i = cdf_read_sat(&info, &h, &sat)) == -1) { + expn = "Can't read SAT"; + goto out0; + } +#ifdef CDF_DEBUG + cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h)); +#endif + + if ((i = cdf_read_ssat(&info, &h, &sat, &ssat)) == -1) { + expn = "Can't read SSAT"; + goto out1; + } +#ifdef CDF_DEBUG + cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h)); +#endif + + if ((i = cdf_read_dir(&info, &h, &sat, &dir)) == -1) { + expn = "Can't read directory"; + goto out2; + } + + const cdf_directory_t *root_storage; + if ((i = cdf_read_short_stream(&info, &h, &sat, &dir, &sst, + &root_storage)) == -1) { + expn = "Cannot read short stream"; + goto out3; + } +#ifdef CDF_DEBUG + cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir); +#endif +#ifdef notdef + if (root_storage) { + if (NOTMIME(ms)) { + char clsbuf[128]; + if (file_printf(ms, "CLSID %s, ", + format_clsid(clsbuf, sizeof(clsbuf), + root_storage->d_storage_uuid)) == -1) + return -1; + } + } +#endif + + if ((i = cdf_read_user_stream(&info, &h, &sat, &ssat, &sst, &dir, + "FileHeader", &scn)) != -1) { +#define HWP5_SIGNATURE "HWP Document File" + if (scn.sst_dirlen >= sizeof(HWP5_SIGNATURE) - 1 + && memcmp(scn.sst_tab, HWP5_SIGNATURE, + sizeof(HWP5_SIGNATURE) - 1) == 0) { + if (NOTMIME(ms)) { + if (file_printf(ms, + "Hangul (Korean) Word Processor File 5.x") == -1) + return -1; + } else { + if (file_printf(ms, "application/x-hwp") == -1) + return -1; + } + i = 1; + goto out5; + } else { + free(scn.sst_tab); + scn.sst_tab = NULL; + scn.sst_len = 0; + scn.sst_dirlen = 0; + } + } + + if ((i = cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir, + &scn)) == -1) { + if (errno == ESRCH) { + corrupt = expn; + expn = "No summary info"; + } else { + expn = "Cannot read summary info"; + } + goto out4; + } +#ifdef CDF_DEBUG + cdf_dump_summary_info(&h, &scn); +#endif + if ((i = cdf_file_summary_info(ms, &h, &scn, root_storage)) < 0) + expn = "Can't expand summary_info"; + + if (i == 0) { + const char *str = NULL; + cdf_directory_t *d; + char name[__arraycount(d->d_name)]; + size_t j, k; + + for (j = 0; str == NULL && j < dir.dir_len; j++) { + d = &dir.dir_tab[j]; + for (k = 0; k < sizeof(name); k++) + name[k] = (char)cdf_tole2(d->d_name[k]); + str = cdf_app_to_mime(name, + NOTMIME(ms) ? name2desc : name2mime); + } + if (NOTMIME(ms)) { + if (str != NULL) { + if (file_printf(ms, "%s", str) == -1) + return -1; + i = 1; + } + } else { + if (str == NULL) + str = "vnd.ms-office"; + if (file_printf(ms, "application/%s", str) == -1) + return -1; + i = 1; + } + } +out5: + free(scn.sst_tab); +out4: + free(sst.sst_tab); +out3: + free(dir.dir_tab); +out2: + free(ssat.sat_tab); +out1: + free(sat.sat_tab); +out0: + if (i == -1) { + if (NOTMIME(ms)) { + if (file_printf(ms, + "Composite Document File V2 Document") == -1) + return -1; + if (*expn) + if (file_printf(ms, ", %s%s", corrupt, expn) == -1) + return -1; + } else { + if (file_printf(ms, "application/CDFV2-corrupt") == -1) + return -1; + } + i = 1; + } + return i; +} diff --git a/contrib/file/src/readelf.c b/contrib/file/src/readelf.c new file mode 100644 index 0000000..ede7e0c --- /dev/null +++ b/contrib/file/src/readelf.c @@ -0,0 +1,1286 @@ +/* + * Copyright (c) Christos Zoulas 2003. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: readelf.c,v 1.103 2014/05/02 02:25:10 christos Exp $") +#endif + +#ifdef BUILTIN_ELF +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "readelf.h" +#include "magic.h" + +#ifdef ELFCORE +private int dophn_core(struct magic_set *, int, int, int, off_t, int, size_t, + off_t, int *); +#endif +private int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t, + off_t, int *, int); +private int doshn(struct magic_set *, int, int, int, off_t, int, size_t, + off_t, int *, int, int); +private size_t donote(struct magic_set *, void *, size_t, size_t, int, + int, size_t, int *); + +#define ELF_ALIGN(a) ((((a) + align - 1) / align) * align) + +#define isquote(c) (strchr("'\"`", (c)) != NULL) + +private uint16_t getu16(int, uint16_t); +private uint32_t getu32(int, uint32_t); +private uint64_t getu64(int, uint64_t); + +private uint16_t +getu16(int swap, uint16_t value) +{ + union { + uint16_t ui; + char c[2]; + } retval, tmpval; + + if (swap) { + tmpval.ui = value; + + retval.c[0] = tmpval.c[1]; + retval.c[1] = tmpval.c[0]; + + return retval.ui; + } else + return value; +} + +private uint32_t +getu32(int swap, uint32_t value) +{ + union { + uint32_t ui; + char c[4]; + } retval, tmpval; + + if (swap) { + tmpval.ui = value; + + retval.c[0] = tmpval.c[3]; + retval.c[1] = tmpval.c[2]; + retval.c[2] = tmpval.c[1]; + retval.c[3] = tmpval.c[0]; + + return retval.ui; + } else + return value; +} + +private uint64_t +getu64(int swap, uint64_t value) +{ + union { + uint64_t ui; + char c[8]; + } retval, tmpval; + + if (swap) { + tmpval.ui = value; + + retval.c[0] = tmpval.c[7]; + retval.c[1] = tmpval.c[6]; + retval.c[2] = tmpval.c[5]; + retval.c[3] = tmpval.c[4]; + retval.c[4] = tmpval.c[3]; + retval.c[5] = tmpval.c[2]; + retval.c[6] = tmpval.c[1]; + retval.c[7] = tmpval.c[0]; + + return retval.ui; + } else + return value; +} + +#define elf_getu16(swap, value) getu16(swap, value) +#define elf_getu32(swap, value) getu32(swap, value) +#define elf_getu64(swap, value) getu64(swap, value) + +#define xsh_addr (clazz == ELFCLASS32 \ + ? (void *)&sh32 \ + : (void *)&sh64) +#define xsh_sizeof (clazz == ELFCLASS32 \ + ? sizeof(sh32) \ + : sizeof(sh64)) +#define xsh_size (size_t)(clazz == ELFCLASS32 \ + ? elf_getu32(swap, sh32.sh_size) \ + : elf_getu64(swap, sh64.sh_size)) +#define xsh_offset (off_t)(clazz == ELFCLASS32 \ + ? elf_getu32(swap, sh32.sh_offset) \ + : elf_getu64(swap, sh64.sh_offset)) +#define xsh_type (clazz == ELFCLASS32 \ + ? elf_getu32(swap, sh32.sh_type) \ + : elf_getu32(swap, sh64.sh_type)) +#define xsh_name (clazz == ELFCLASS32 \ + ? elf_getu32(swap, sh32.sh_name) \ + : elf_getu32(swap, sh64.sh_name)) +#define xph_addr (clazz == ELFCLASS32 \ + ? (void *) &ph32 \ + : (void *) &ph64) +#define xph_sizeof (clazz == ELFCLASS32 \ + ? sizeof(ph32) \ + : sizeof(ph64)) +#define xph_type (clazz == ELFCLASS32 \ + ? elf_getu32(swap, ph32.p_type) \ + : elf_getu32(swap, ph64.p_type)) +#define xph_offset (off_t)(clazz == ELFCLASS32 \ + ? elf_getu32(swap, ph32.p_offset) \ + : elf_getu64(swap, ph64.p_offset)) +#define xph_align (size_t)((clazz == ELFCLASS32 \ + ? (off_t) (ph32.p_align ? \ + elf_getu32(swap, ph32.p_align) : 4) \ + : (off_t) (ph64.p_align ? \ + elf_getu64(swap, ph64.p_align) : 4))) +#define xph_filesz (size_t)((clazz == ELFCLASS32 \ + ? elf_getu32(swap, ph32.p_filesz) \ + : elf_getu64(swap, ph64.p_filesz))) +#define xnh_addr (clazz == ELFCLASS32 \ + ? (void *)&nh32 \ + : (void *)&nh64) +#define xph_memsz (size_t)((clazz == ELFCLASS32 \ + ? elf_getu32(swap, ph32.p_memsz) \ + : elf_getu64(swap, ph64.p_memsz))) +#define xnh_sizeof (clazz == ELFCLASS32 \ + ? sizeof nh32 \ + : sizeof nh64) +#define xnh_type (clazz == ELFCLASS32 \ + ? elf_getu32(swap, nh32.n_type) \ + : elf_getu32(swap, nh64.n_type)) +#define xnh_namesz (clazz == ELFCLASS32 \ + ? elf_getu32(swap, nh32.n_namesz) \ + : elf_getu32(swap, nh64.n_namesz)) +#define xnh_descsz (clazz == ELFCLASS32 \ + ? elf_getu32(swap, nh32.n_descsz) \ + : elf_getu32(swap, nh64.n_descsz)) +#define prpsoffsets(i) (clazz == ELFCLASS32 \ + ? prpsoffsets32[i] \ + : prpsoffsets64[i]) +#define xcap_addr (clazz == ELFCLASS32 \ + ? (void *)&cap32 \ + : (void *)&cap64) +#define xcap_sizeof (clazz == ELFCLASS32 \ + ? sizeof cap32 \ + : sizeof cap64) +#define xcap_tag (clazz == ELFCLASS32 \ + ? elf_getu32(swap, cap32.c_tag) \ + : elf_getu64(swap, cap64.c_tag)) +#define xcap_val (clazz == ELFCLASS32 \ + ? elf_getu32(swap, cap32.c_un.c_val) \ + : elf_getu64(swap, cap64.c_un.c_val)) + +#ifdef ELFCORE +/* + * Try larger offsets first to avoid false matches + * from earlier data that happen to look like strings. + */ +static const size_t prpsoffsets32[] = { +#ifdef USE_NT_PSINFO + 104, /* SunOS 5.x (command line) */ + 88, /* SunOS 5.x (short name) */ +#endif /* USE_NT_PSINFO */ + + 100, /* SunOS 5.x (command line) */ + 84, /* SunOS 5.x (short name) */ + + 44, /* Linux (command line) */ + 28, /* Linux 2.0.36 (short name) */ + + 8, /* FreeBSD */ +}; + +static const size_t prpsoffsets64[] = { +#ifdef USE_NT_PSINFO + 152, /* SunOS 5.x (command line) */ + 136, /* SunOS 5.x (short name) */ +#endif /* USE_NT_PSINFO */ + + 136, /* SunOS 5.x, 64-bit (command line) */ + 120, /* SunOS 5.x, 64-bit (short name) */ + + 56, /* Linux (command line) */ + 40, /* Linux (tested on core from 2.4.x, short name) */ + + 16, /* FreeBSD, 64-bit */ +}; + +#define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0]) +#define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0]) + +#define NOFFSETS (clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64) + +/* + * Look through the program headers of an executable image, searching + * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or + * "FreeBSD"; if one is found, try looking in various places in its + * contents for a 16-character string containing only printable + * characters - if found, that string should be the name of the program + * that dropped core. Note: right after that 16-character string is, + * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and + * Linux, a longer string (80 characters, in 5.x, probably other + * SVR4-flavored systems, and Linux) containing the start of the + * command line for that program. + * + * SunOS 5.x core files contain two PT_NOTE sections, with the types + * NT_PRPSINFO (old) and NT_PSINFO (new). These structs contain the + * same info about the command name and command line, so it probably + * isn't worthwhile to look for NT_PSINFO, but the offsets are provided + * above (see USE_NT_PSINFO), in case we ever decide to do so. The + * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent; + * the SunOS 5.x file command relies on this (and prefers the latter). + * + * The signal number probably appears in a section of type NT_PRSTATUS, + * but that's also rather OS-dependent, in ways that are harder to + * dissect with heuristics, so I'm not bothering with the signal number. + * (I suppose the signal number could be of interest in situations where + * you don't have the binary of the program that dropped core; if you + * *do* have that binary, the debugger will probably tell you what + * signal it was.) + */ + +#define OS_STYLE_SVR4 0 +#define OS_STYLE_FREEBSD 1 +#define OS_STYLE_NETBSD 2 + +private const char os_style_names[][8] = { + "SVR4", + "FreeBSD", + "NetBSD", +}; + +#define FLAGS_DID_CORE 0x01 +#define FLAGS_DID_NOTE 0x02 +#define FLAGS_DID_BUILD_ID 0x04 +#define FLAGS_DID_CORE_STYLE 0x08 +#define FLAGS_IS_CORE 0x10 + +private int +dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, + int num, size_t size, off_t fsize, int *flags) +{ + Elf32_Phdr ph32; + Elf64_Phdr ph64; + size_t offset, len; + unsigned char nbuf[BUFSIZ]; + ssize_t bufsize; + + if (size != xph_sizeof) { + if (file_printf(ms, ", corrupted program header size") == -1) + return -1; + return 0; + } + + /* + * Loop through all the program headers. + */ + for ( ; num; num--) { + if (pread(fd, xph_addr, xph_sizeof, off) == -1) { + file_badread(ms); + return -1; + } + off += size; + + if (xph_offset > fsize) { + /* Perhaps warn here */ + continue; + } + + if (xph_type != PT_NOTE) + continue; + + /* + * This is a PT_NOTE section; loop through all the notes + * in the section. + */ + len = xph_filesz < sizeof(nbuf) ? xph_filesz : sizeof(nbuf); + if ((bufsize = pread(fd, nbuf, len, xph_offset)) == -1) { + file_badread(ms); + return -1; + } + offset = 0; + for (;;) { + if (offset >= (size_t)bufsize) + break; + offset = donote(ms, nbuf, offset, (size_t)bufsize, + clazz, swap, 4, flags); + if (offset == 0) + break; + + } + } + return 0; +} +#endif + +static void +do_note_netbsd_version(struct magic_set *ms, int swap, void *v) +{ + uint32_t desc; + (void)memcpy(&desc, v, sizeof(desc)); + desc = elf_getu32(swap, desc); + + if (file_printf(ms, ", for NetBSD") == -1) + return; + /* + * The version number used to be stuck as 199905, and was thus + * basically content-free. Newer versions of NetBSD have fixed + * this and now use the encoding of __NetBSD_Version__: + * + * MMmmrrpp00 + * + * M = major version + * m = minor version + * r = release ["",A-Z,Z[A-Z] but numeric] + * p = patchlevel + */ + if (desc > 100000000U) { + uint32_t ver_patch = (desc / 100) % 100; + uint32_t ver_rel = (desc / 10000) % 100; + uint32_t ver_min = (desc / 1000000) % 100; + uint32_t ver_maj = desc / 100000000; + + if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1) + return; + if (ver_rel == 0 && ver_patch != 0) { + if (file_printf(ms, ".%u", ver_patch) == -1) + return; + } else if (ver_rel != 0) { + while (ver_rel > 26) { + if (file_printf(ms, "Z") == -1) + return; + ver_rel -= 26; + } + if (file_printf(ms, "%c", 'A' + ver_rel - 1) + == -1) + return; + } + } +} + +static void +do_note_freebsd_version(struct magic_set *ms, int swap, void *v) +{ + uint32_t desc; + + (void)memcpy(&desc, v, sizeof(desc)); + desc = elf_getu32(swap, desc); + if (file_printf(ms, ", for FreeBSD") == -1) + return; + + /* + * Contents is __FreeBSD_version, whose relation to OS + * versions is defined by a huge table in the Porter's + * Handbook. This is the general scheme: + * + * Releases: + * Mmp000 (before 4.10) + * Mmi0p0 (before 5.0) + * Mmm0p0 + * + * Development branches: + * Mmpxxx (before 4.6) + * Mmp1xx (before 4.10) + * Mmi1xx (before 5.0) + * M000xx (pre-M.0) + * Mmm1xx + * + * M = major version + * m = minor version + * i = minor version increment (491000 -> 4.10) + * p = patchlevel + * x = revision + * + * The first release of FreeBSD to use ELF by default + * was version 3.0. + */ + if (desc == 460002) { + if (file_printf(ms, " 4.6.2") == -1) + return; + } else if (desc < 460100) { + if (file_printf(ms, " %d.%d", desc / 100000, + desc / 10000 % 10) == -1) + return; + if (desc / 1000 % 10 > 0) + if (file_printf(ms, ".%d", desc / 1000 % 10) == -1) + return; + if ((desc % 1000 > 0) || (desc % 100000 == 0)) + if (file_printf(ms, " (%d)", desc) == -1) + return; + } else if (desc < 500000) { + if (file_printf(ms, " %d.%d", desc / 100000, + desc / 10000 % 10 + desc / 1000 % 10) == -1) + return; + if (desc / 100 % 10 > 0) { + if (file_printf(ms, " (%d)", desc) == -1) + return; + } else if (desc / 10 % 10 > 0) { + if (file_printf(ms, ".%d", desc / 10 % 10) == -1) + return; + } + } else { + if (file_printf(ms, " %d.%d", desc / 100000, + desc / 1000 % 100) == -1) + return; + if ((desc / 100 % 10 > 0) || + (desc % 100000 / 100 == 0)) { + if (file_printf(ms, " (%d)", desc) == -1) + return; + } else if (desc / 10 % 10 > 0) { + if (file_printf(ms, ".%d", desc / 10 % 10) == -1) + return; + } + } +} + +private size_t +donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, + int clazz, int swap, size_t align, int *flags) +{ + Elf32_Nhdr nh32; + Elf64_Nhdr nh64; + size_t noff, doff; +#ifdef ELFCORE + int os_style = -1; +#endif + uint32_t namesz, descsz; + unsigned char *nbuf = CAST(unsigned char *, vbuf); + + (void)memcpy(xnh_addr, &nbuf[offset], xnh_sizeof); + offset += xnh_sizeof; + + namesz = xnh_namesz; + descsz = xnh_descsz; + if ((namesz == 0) && (descsz == 0)) { + /* + * We're out of note headers. + */ + return (offset >= size) ? offset : size; + } + + if (namesz & 0x80000000) { + (void)file_printf(ms, ", bad note name size 0x%lx", + (unsigned long)namesz); + return offset; + } + + if (descsz & 0x80000000) { + (void)file_printf(ms, ", bad note description size 0x%lx", + (unsigned long)descsz); + return offset; + } + + + noff = offset; + doff = ELF_ALIGN(offset + namesz); + + if (offset + namesz > size) { + /* + * We're past the end of the buffer. + */ + return doff; + } + + offset = ELF_ALIGN(doff + descsz); + if (doff + descsz > size) { + /* + * We're past the end of the buffer. + */ + return (offset >= size) ? offset : size; + } + + if ((*flags & (FLAGS_DID_NOTE|FLAGS_DID_BUILD_ID)) == + (FLAGS_DID_NOTE|FLAGS_DID_BUILD_ID)) + goto core; + + if (namesz == 5 && strcmp((char *)&nbuf[noff], "SuSE") == 0 && + xnh_type == NT_GNU_VERSION && descsz == 2) { + file_printf(ms, ", for SuSE %d.%d", nbuf[doff], nbuf[doff + 1]); + } + if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && + xnh_type == NT_GNU_VERSION && descsz == 16) { + uint32_t desc[4]; + (void)memcpy(desc, &nbuf[doff], sizeof(desc)); + + if (file_printf(ms, ", for GNU/") == -1) + return size; + switch (elf_getu32(swap, desc[0])) { + case GNU_OS_LINUX: + if (file_printf(ms, "Linux") == -1) + return size; + break; + case GNU_OS_HURD: + if (file_printf(ms, "Hurd") == -1) + return size; + break; + case GNU_OS_SOLARIS: + if (file_printf(ms, "Solaris") == -1) + return size; + break; + case GNU_OS_KFREEBSD: + if (file_printf(ms, "kFreeBSD") == -1) + return size; + break; + case GNU_OS_KNETBSD: + if (file_printf(ms, "kNetBSD") == -1) + return size; + break; + default: + if (file_printf(ms, "<unknown>") == -1) + return size; + } + if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]), + elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1) + return size; + *flags |= FLAGS_DID_NOTE; + return size; + } + + if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && + xnh_type == NT_GNU_BUILD_ID && (descsz == 16 || descsz == 20)) { + uint8_t desc[20]; + uint32_t i; + if (file_printf(ms, ", BuildID[%s]=", descsz == 16 ? "md5/uuid" : + "sha1") == -1) + return size; + (void)memcpy(desc, &nbuf[doff], descsz); + for (i = 0; i < descsz; i++) + if (file_printf(ms, "%02x", desc[i]) == -1) + return size; + *flags |= FLAGS_DID_BUILD_ID; + } + + if (namesz == 4 && strcmp((char *)&nbuf[noff], "PaX") == 0 && + xnh_type == NT_NETBSD_PAX && descsz == 4) { + static const char *pax[] = { + "+mprotect", + "-mprotect", + "+segvguard", + "-segvguard", + "+ASLR", + "-ASLR", + }; + uint32_t desc; + size_t i; + int did = 0; + + (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); + desc = elf_getu32(swap, desc); + + if (desc && file_printf(ms, ", PaX: ") == -1) + return size; + + for (i = 0; i < __arraycount(pax); i++) { + if (((1 << i) & desc) == 0) + continue; + if (file_printf(ms, "%s%s", did++ ? "," : "", + pax[i]) == -1) + return size; + } + } + + if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0) { + switch (xnh_type) { + case NT_NETBSD_VERSION: + if (descsz == 4) { + do_note_netbsd_version(ms, swap, &nbuf[doff]); + *flags |= FLAGS_DID_NOTE; + return size; + } + break; + case NT_NETBSD_MARCH: + if (file_printf(ms, ", compiled for: %.*s", (int)descsz, + (const char *)&nbuf[doff]) == -1) + return size; + break; + case NT_NETBSD_CMODEL: + if (file_printf(ms, ", compiler model: %.*s", + (int)descsz, (const char *)&nbuf[doff]) == -1) + return size; + break; + default: + if (file_printf(ms, ", note=%u", xnh_type) == -1) + return size; + break; + } + return size; + } + + if (namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0) { + if (xnh_type == NT_FREEBSD_VERSION && descsz == 4) { + do_note_freebsd_version(ms, swap, &nbuf[doff]); + *flags |= FLAGS_DID_NOTE; + return size; + } + } + + if (namesz == 8 && strcmp((char *)&nbuf[noff], "OpenBSD") == 0 && + xnh_type == NT_OPENBSD_VERSION && descsz == 4) { + if (file_printf(ms, ", for OpenBSD") == -1) + return size; + /* Content of note is always 0 */ + *flags |= FLAGS_DID_NOTE; + return size; + } + + if (namesz == 10 && strcmp((char *)&nbuf[noff], "DragonFly") == 0 && + xnh_type == NT_DRAGONFLY_VERSION && descsz == 4) { + uint32_t desc; + if (file_printf(ms, ", for DragonFly") == -1) + return size; + (void)memcpy(&desc, &nbuf[doff], sizeof(desc)); + desc = elf_getu32(swap, desc); + if (file_printf(ms, " %d.%d.%d", desc / 100000, + desc / 10000 % 10, desc % 10000) == -1) + return size; + *flags |= FLAGS_DID_NOTE; + return size; + } + +core: + /* + * Sigh. The 2.0.36 kernel in Debian 2.1, at + * least, doesn't correctly implement name + * sections, in core dumps, as specified by + * the "Program Linking" section of "UNIX(R) System + * V Release 4 Programmer's Guide: ANSI C and + * Programming Support Tools", because my copy + * clearly says "The first 'namesz' bytes in 'name' + * contain a *null-terminated* [emphasis mine] + * character representation of the entry's owner + * or originator", but the 2.0.36 kernel code + * doesn't include the terminating null in the + * name.... + */ + if ((namesz == 4 && strncmp((char *)&nbuf[noff], "CORE", 4) == 0) || + (namesz == 5 && strcmp((char *)&nbuf[noff], "CORE") == 0)) { + os_style = OS_STYLE_SVR4; + } + + if ((namesz == 8 && strcmp((char *)&nbuf[noff], "FreeBSD") == 0)) { + os_style = OS_STYLE_FREEBSD; + } + + if ((namesz >= 11 && strncmp((char *)&nbuf[noff], "NetBSD-CORE", 11) + == 0)) { + os_style = OS_STYLE_NETBSD; + } + +#ifdef ELFCORE + if ((*flags & FLAGS_DID_CORE) != 0) + return size; + + if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) { + if (file_printf(ms, ", %s-style", os_style_names[os_style]) + == -1) + return size; + *flags |= FLAGS_DID_CORE_STYLE; + } + + switch (os_style) { + case OS_STYLE_NETBSD: + if (xnh_type == NT_NETBSD_CORE_PROCINFO) { + uint32_t signo; + /* + * Extract the program name. It is at + * offset 0x7c, and is up to 32-bytes, + * including the terminating NUL. + */ + if (file_printf(ms, ", from '%.31s'", + &nbuf[doff + 0x7c]) == -1) + return size; + + /* + * Extract the signal number. It is at + * offset 0x08. + */ + (void)memcpy(&signo, &nbuf[doff + 0x08], + sizeof(signo)); + if (file_printf(ms, " (signal %u)", + elf_getu32(swap, signo)) == -1) + return size; + *flags |= FLAGS_DID_CORE; + return size; + } + break; + + default: + if (xnh_type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) { + size_t i, j; + unsigned char c; + /* + * Extract the program name. We assume + * it to be 16 characters (that's what it + * is in SunOS 5.x and Linux). + * + * Unfortunately, it's at a different offset + * in various OSes, so try multiple offsets. + * If the characters aren't all printable, + * reject it. + */ + for (i = 0; i < NOFFSETS; i++) { + unsigned char *cname, *cp; + size_t reloffset = prpsoffsets(i); + size_t noffset = doff + reloffset; + size_t k; + for (j = 0; j < 16; j++, noffset++, + reloffset++) { + /* + * Make sure we're not past + * the end of the buffer; if + * we are, just give up. + */ + if (noffset >= size) + goto tryanother; + + /* + * Make sure we're not past + * the end of the contents; + * if we are, this obviously + * isn't the right offset. + */ + if (reloffset >= descsz) + goto tryanother; + + c = nbuf[noffset]; + if (c == '\0') { + /* + * A '\0' at the + * beginning is + * obviously wrong. + * Any other '\0' + * means we're done. + */ + if (j == 0) + goto tryanother; + else + break; + } else { + /* + * A nonprintable + * character is also + * wrong. + */ + if (!isprint(c) || isquote(c)) + goto tryanother; + } + } + /* + * Well, that worked. + */ + + /* + * Try next offsets, in case this match is + * in the middle of a string. + */ + for (k = i + 1 ; k < NOFFSETS ; k++) { + size_t no; + int adjust = 1; + if (prpsoffsets(k) >= prpsoffsets(i)) + continue; + for (no = doff + prpsoffsets(k); + no < doff + prpsoffsets(i); no++) + adjust = adjust + && isprint(nbuf[no]); + if (adjust) + i = k; + } + + cname = (unsigned char *) + &nbuf[doff + prpsoffsets(i)]; + for (cp = cname; *cp && isprint(*cp); cp++) + continue; + /* + * Linux apparently appends a space at the end + * of the command line: remove it. + */ + while (cp > cname && isspace(cp[-1])) + cp--; + if (file_printf(ms, ", from '%.*s'", + (int)(cp - cname), cname) == -1) + return size; + *flags |= FLAGS_DID_CORE; + return size; + + tryanother: + ; + } + } + break; + } +#endif + return offset; +} + +/* SunOS 5.x hardware capability descriptions */ +typedef struct cap_desc { + uint64_t cd_mask; + const char *cd_name; +} cap_desc_t; + +static const cap_desc_t cap_desc_sparc[] = { + { AV_SPARC_MUL32, "MUL32" }, + { AV_SPARC_DIV32, "DIV32" }, + { AV_SPARC_FSMULD, "FSMULD" }, + { AV_SPARC_V8PLUS, "V8PLUS" }, + { AV_SPARC_POPC, "POPC" }, + { AV_SPARC_VIS, "VIS" }, + { AV_SPARC_VIS2, "VIS2" }, + { AV_SPARC_ASI_BLK_INIT, "ASI_BLK_INIT" }, + { AV_SPARC_FMAF, "FMAF" }, + { AV_SPARC_FJFMAU, "FJFMAU" }, + { AV_SPARC_IMA, "IMA" }, + { 0, NULL } +}; + +static const cap_desc_t cap_desc_386[] = { + { AV_386_FPU, "FPU" }, + { AV_386_TSC, "TSC" }, + { AV_386_CX8, "CX8" }, + { AV_386_SEP, "SEP" }, + { AV_386_AMD_SYSC, "AMD_SYSC" }, + { AV_386_CMOV, "CMOV" }, + { AV_386_MMX, "MMX" }, + { AV_386_AMD_MMX, "AMD_MMX" }, + { AV_386_AMD_3DNow, "AMD_3DNow" }, + { AV_386_AMD_3DNowx, "AMD_3DNowx" }, + { AV_386_FXSR, "FXSR" }, + { AV_386_SSE, "SSE" }, + { AV_386_SSE2, "SSE2" }, + { AV_386_PAUSE, "PAUSE" }, + { AV_386_SSE3, "SSE3" }, + { AV_386_MON, "MON" }, + { AV_386_CX16, "CX16" }, + { AV_386_AHF, "AHF" }, + { AV_386_TSCP, "TSCP" }, + { AV_386_AMD_SSE4A, "AMD_SSE4A" }, + { AV_386_POPCNT, "POPCNT" }, + { AV_386_AMD_LZCNT, "AMD_LZCNT" }, + { AV_386_SSSE3, "SSSE3" }, + { AV_386_SSE4_1, "SSE4.1" }, + { AV_386_SSE4_2, "SSE4.2" }, + { 0, NULL } +}; + +private int +doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, + size_t size, off_t fsize, int *flags, int mach, int strtab) +{ + Elf32_Shdr sh32; + Elf64_Shdr sh64; + int stripped = 1; + void *nbuf; + off_t noff, coff, name_off; + uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilites */ + uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilites */ + char name[50]; + + if (size != xsh_sizeof) { + if (file_printf(ms, ", corrupted section header size") == -1) + return -1; + return 0; + } + + /* Read offset of name section to be able to read section names later */ + if (pread(fd, xsh_addr, xsh_sizeof, off + size * strtab) == -1) { + file_badread(ms); + return -1; + } + name_off = xsh_offset; + + for ( ; num; num--) { + /* Read the name of this section. */ + if (pread(fd, name, sizeof(name), name_off + xsh_name) == -1) { + file_badread(ms); + return -1; + } + name[sizeof(name) - 1] = '\0'; + if (strcmp(name, ".debug_info") == 0) + stripped = 0; + + if (pread(fd, xsh_addr, xsh_sizeof, off) == -1) { + file_badread(ms); + return -1; + } + off += size; + + /* Things we can determine before we seek */ + switch (xsh_type) { + case SHT_SYMTAB: +#if 0 + case SHT_DYNSYM: +#endif + stripped = 0; + break; + default: + if (xsh_offset > fsize) { + /* Perhaps warn here */ + continue; + } + break; + } + + /* Things we can determine when we seek */ + switch (xsh_type) { + case SHT_NOTE: + if ((nbuf = malloc(xsh_size)) == NULL) { + file_error(ms, errno, "Cannot allocate memory" + " for note"); + return -1; + } + if (pread(fd, nbuf, xsh_size, xsh_offset) == -1) { + file_badread(ms); + free(nbuf); + return -1; + } + + noff = 0; + for (;;) { + if (noff >= (off_t)xsh_size) + break; + noff = donote(ms, nbuf, (size_t)noff, + xsh_size, clazz, swap, 4, flags); + if (noff == 0) + break; + } + free(nbuf); + break; + case SHT_SUNW_cap: + switch (mach) { + case EM_SPARC: + case EM_SPARCV9: + case EM_IA_64: + case EM_386: + case EM_AMD64: + break; + default: + goto skip; + } + + if (lseek(fd, xsh_offset, SEEK_SET) == (off_t)-1) { + file_badseek(ms); + return -1; + } + coff = 0; + for (;;) { + Elf32_Cap cap32; + Elf64_Cap cap64; + char cbuf[/*CONSTCOND*/ + MAX(sizeof cap32, sizeof cap64)]; + if ((coff += xcap_sizeof) > (off_t)xsh_size) + break; + if (read(fd, cbuf, (size_t)xcap_sizeof) != + (ssize_t)xcap_sizeof) { + file_badread(ms); + return -1; + } + if (cbuf[0] == 'A') { +#ifdef notyet + char *p = cbuf + 1; + uint32_t len, tag; + memcpy(&len, p, sizeof(len)); + p += 4; + len = getu32(swap, len); + if (memcmp("gnu", p, 3) != 0) { + if (file_printf(ms, + ", unknown capability %.3s", p) + == -1) + return -1; + break; + } + p += strlen(p) + 1; + tag = *p++; + memcpy(&len, p, sizeof(len)); + p += 4; + len = getu32(swap, len); + if (tag != 1) { + if (file_printf(ms, ", unknown gnu" + " capability tag %d", tag) + == -1) + return -1; + break; + } + // gnu attributes +#endif + break; + } + (void)memcpy(xcap_addr, cbuf, xcap_sizeof); + switch (xcap_tag) { + case CA_SUNW_NULL: + break; + case CA_SUNW_HW_1: + cap_hw1 |= xcap_val; + break; + case CA_SUNW_SF_1: + cap_sf1 |= xcap_val; + break; + default: + if (file_printf(ms, + ", with unknown capability " + "0x%" INT64_T_FORMAT "x = 0x%" + INT64_T_FORMAT "x", + (unsigned long long)xcap_tag, + (unsigned long long)xcap_val) == -1) + return -1; + break; + } + } + /*FALLTHROUGH*/ + skip: + default: + break; + } + } + + if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1) + return -1; + if (cap_hw1) { + const cap_desc_t *cdp; + switch (mach) { + case EM_SPARC: + case EM_SPARC32PLUS: + case EM_SPARCV9: + cdp = cap_desc_sparc; + break; + case EM_386: + case EM_IA_64: + case EM_AMD64: + cdp = cap_desc_386; + break; + default: + cdp = NULL; + break; + } + if (file_printf(ms, ", uses") == -1) + return -1; + if (cdp) { + while (cdp->cd_name) { + if (cap_hw1 & cdp->cd_mask) { + if (file_printf(ms, + " %s", cdp->cd_name) == -1) + return -1; + cap_hw1 &= ~cdp->cd_mask; + } + ++cdp; + } + if (cap_hw1) + if (file_printf(ms, + " unknown hardware capability 0x%" + INT64_T_FORMAT "x", + (unsigned long long)cap_hw1) == -1) + return -1; + } else { + if (file_printf(ms, + " hardware capability 0x%" INT64_T_FORMAT "x", + (unsigned long long)cap_hw1) == -1) + return -1; + } + } + if (cap_sf1) { + if (cap_sf1 & SF1_SUNW_FPUSED) { + if (file_printf(ms, + (cap_sf1 & SF1_SUNW_FPKNWN) + ? ", uses frame pointer" + : ", not known to use frame pointer") == -1) + return -1; + } + cap_sf1 &= ~SF1_SUNW_MASK; + if (cap_sf1) + if (file_printf(ms, + ", with unknown software capability 0x%" + INT64_T_FORMAT "x", + (unsigned long long)cap_sf1) == -1) + return -1; + } + return 0; +} + +/* + * Look through the program headers of an executable image, searching + * for a PT_INTERP section; if one is found, it's dynamically linked, + * otherwise it's statically linked. + */ +private int +dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off, + int num, size_t size, off_t fsize, int *flags, int sh_num) +{ + Elf32_Phdr ph32; + Elf64_Phdr ph64; + const char *linking_style = "statically"; + const char *shared_libraries = ""; + unsigned char nbuf[BUFSIZ]; + ssize_t bufsize; + size_t offset, align, len; + + if (size != xph_sizeof) { + if (file_printf(ms, ", corrupted program header size") == -1) + return -1; + return 0; + } + + for ( ; num; num--) { + if (pread(fd, xph_addr, xph_sizeof, off) == -1) { + file_badread(ms); + return -1; + } + + off += size; + + /* Things we can determine before we seek */ + switch (xph_type) { + case PT_DYNAMIC: + linking_style = "dynamically"; + break; + case PT_INTERP: + shared_libraries = " (uses shared libs)"; + break; + default: + if (xph_offset > fsize) { + /* Maybe warn here? */ + continue; + } + break; + } + + /* Things we can determine when we seek */ + switch (xph_type) { + case PT_NOTE: + if ((align = xph_align) & 0x80000000UL) { + if (file_printf(ms, + ", invalid note alignment 0x%lx", + (unsigned long)align) == -1) + return -1; + align = 4; + } + if (sh_num) + break; + /* + * This is a PT_NOTE section; loop through all the notes + * in the section. + */ + len = xph_filesz < sizeof(nbuf) ? xph_filesz + : sizeof(nbuf); + bufsize = pread(fd, nbuf, len, xph_offset); + if (bufsize == -1) { + file_badread(ms); + return -1; + } + offset = 0; + for (;;) { + if (offset >= (size_t)bufsize) + break; + offset = donote(ms, nbuf, offset, + (size_t)bufsize, clazz, swap, align, + flags); + if (offset == 0) + break; + } + break; + default: + break; + } + } + if (file_printf(ms, ", %s linked%s", linking_style, shared_libraries) + == -1) + return -1; + return 0; +} + + +protected int +file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf, + size_t nbytes) +{ + union { + int32_t l; + char c[sizeof (int32_t)]; + } u; + int clazz; + int swap; + struct stat st; + off_t fsize; + int flags = 0; + Elf32_Ehdr elf32hdr; + Elf64_Ehdr elf64hdr; + uint16_t type; + + if (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) + return 0; + /* + * ELF executables have multiple section headers in arbitrary + * file locations and thus file(1) cannot determine it from easily. + * Instead we traverse thru all section headers until a symbol table + * one is found or else the binary is stripped. + * Return immediately if it's not ELF (so we avoid pipe2file unless needed). + */ + if (buf[EI_MAG0] != ELFMAG0 + || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1) + || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) + return 0; + + /* + * If we cannot seek, it must be a pipe, socket or fifo. + */ + if((lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) && (errno == ESPIPE)) + fd = file_pipe2file(ms, fd, buf, nbytes); + + if (fstat(fd, &st) == -1) { + file_badread(ms); + return -1; + } + fsize = st.st_size; + + clazz = buf[EI_CLASS]; + + switch (clazz) { + case ELFCLASS32: +#undef elf_getu +#define elf_getu(a, b) elf_getu32(a, b) +#undef elfhdr +#define elfhdr elf32hdr +#include "elfclass.h" + case ELFCLASS64: +#undef elf_getu +#define elf_getu(a, b) elf_getu64(a, b) +#undef elfhdr +#define elfhdr elf64hdr +#include "elfclass.h" + default: + if (file_printf(ms, ", unknown class %d", clazz) == -1) + return -1; + break; + } + return 0; +} +#endif diff --git a/contrib/file/src/readelf.h b/contrib/file/src/readelf.h new file mode 100644 index 0000000..732b20c --- /dev/null +++ b/contrib/file/src/readelf.h @@ -0,0 +1,365 @@ +/* + * Copyright (c) Christos Zoulas 2003. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * @(#)Id: readelf.h,v 1.9 2002/05/16 18:45:56 christos Exp + * + * Provide elf data structures for non-elf machines, allowing file + * non-elf hosts to determine if an elf binary is stripped. + * Note: cobbled from the linux header file, with modifications + */ +#ifndef __fake_elf_h__ +#define __fake_elf_h__ + +#if HAVE_STDINT_H +#include <stdint.h> +#endif + +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Word; +typedef uint8_t Elf32_Char; + +typedef uint64_t Elf64_Addr; +typedef uint64_t Elf64_Off; +typedef uint64_t Elf64_Xword; +typedef uint16_t Elf64_Half; +typedef uint32_t Elf64_Word; +typedef uint8_t Elf64_Char; + +#define EI_NIDENT 16 + +typedef struct { + Elf32_Char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; /* Entry point */ + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + Elf64_Char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; /* Entry point */ + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* e_type */ +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 + +/* e_machine (used only for SunOS 5.x hardware capabilities) */ +#define EM_SPARC 2 +#define EM_386 3 +#define EM_SPARC32PLUS 18 +#define EM_SPARCV9 43 +#define EM_IA_64 50 +#define EM_AMD64 62 + +/* sh_type */ +#define SHT_SYMTAB 2 +#define SHT_NOTE 7 +#define SHT_DYNSYM 11 +#define SHT_SUNW_cap 0x6ffffff5 /* SunOS 5.x hw/sw capabilites */ + +/* elf type */ +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +/* elf class */ +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +/* magic number */ +#define EI_MAG0 0 /* e_ident[] indexes */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_PAD 7 + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" + +#define OLFMAG1 'O' +#define OLFMAG "\177OLF" + +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +#define PT_NULL 0 /* p_type */ +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_NUM 7 + +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Off sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Off sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Off sh_addralign; + Elf64_Off sh_entsize; +} Elf64_Shdr; + +#define NT_NETBSD_CORE_PROCINFO 1 + +/* Note header in a PT_NOTE section */ +typedef struct elf_note { + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ +} Elf32_Nhdr; + +typedef struct { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +} Elf64_Nhdr; + +/* Notes used in ET_CORE */ +#define NT_PRSTATUS 1 +#define NT_PRFPREG 2 +#define NT_PRPSINFO 3 +#define NT_PRXREG 4 +#define NT_TASKSTRUCT 4 +#define NT_PLATFORM 5 +#define NT_AUXV 6 + +/* Note types used in executables */ +/* NetBSD executables (name = "NetBSD") */ +#define NT_NETBSD_VERSION 1 +#define NT_NETBSD_EMULATION 2 +#define NT_FREEBSD_VERSION 1 +#define NT_OPENBSD_VERSION 1 +#define NT_DRAGONFLY_VERSION 1 +/* + * GNU executables (name = "GNU") + * word[0]: GNU OS tags + * word[1]: major version + * word[2]: minor version + * word[3]: tiny version + */ +#define NT_GNU_VERSION 1 + +/* GNU OS tags */ +#define GNU_OS_LINUX 0 +#define GNU_OS_HURD 1 +#define GNU_OS_SOLARIS 2 +#define GNU_OS_KFREEBSD 3 +#define GNU_OS_KNETBSD 4 + +/* + * GNU Hardware capability information + * word[0]: Number of entries + * word[1]: Bitmask of enabled entries + * Followed by a byte id, and a NUL terminated string per entry + */ +#define NT_GNU_HWCAP 2 + +/* + * GNU Build ID generated by ld + * 160 bit SHA1 [default] + * 128 bit md5 or uuid + */ +#define NT_GNU_BUILD_ID 3 + +/* + * NetBSD-specific note type: PaX. + * There should be 1 NOTE per executable. + * name: PaX\0 + * namesz: 4 + * desc: + * word[0]: capability bitmask + * descsz: 4 + */ +#define NT_NETBSD_PAX 3 +#define NT_NETBSD_PAX_MPROTECT 0x01 /* Force enable Mprotect */ +#define NT_NETBSD_PAX_NOMPROTECT 0x02 /* Force disable Mprotect */ +#define NT_NETBSD_PAX_GUARD 0x04 /* Force enable Segvguard */ +#define NT_NETBSD_PAX_NOGUARD 0x08 /* Force disable Servguard */ +#define NT_NETBSD_PAX_ASLR 0x10 /* Force enable ASLR */ +#define NT_NETBSD_PAX_NOASLR 0x20 /* Force disable ASLR */ + +/* + * NetBSD-specific note type: MACHINE_ARCH. + * There should be 1 NOTE per executable. + * name: NetBSD\0 + * namesz: 7 + * desc: string + * descsz: variable + */ +#define NT_NETBSD_MARCH 5 + +/* + * NetBSD-specific note type: COMPILER MODEL. + * There should be 1 NOTE per executable. + * name: NetBSD\0 + * namesz: 7 + * desc: string + * descsz: variable + */ +#define NT_NETBSD_CMODEL 6 + +#if !defined(ELFSIZE) && defined(ARCH_ELFSIZE) +#define ELFSIZE ARCH_ELFSIZE +#endif +/* SunOS 5.x hardware/software capabilities */ +typedef struct { + Elf32_Word c_tag; + union { + Elf32_Word c_val; + Elf32_Addr c_ptr; + } c_un; +} Elf32_Cap; + +typedef struct { + Elf64_Xword c_tag; + union { + Elf64_Xword c_val; + Elf64_Addr c_ptr; + } c_un; +} Elf64_Cap; + +/* SunOS 5.x hardware/software capability tags */ +#define CA_SUNW_NULL 0 +#define CA_SUNW_HW_1 1 +#define CA_SUNW_SF_1 2 + +/* SunOS 5.x software capabilities */ +#define SF1_SUNW_FPKNWN 0x01 +#define SF1_SUNW_FPUSED 0x02 +#define SF1_SUNW_MASK 0x03 + +/* SunOS 5.x hardware capabilities: sparc */ +#define AV_SPARC_MUL32 0x0001 +#define AV_SPARC_DIV32 0x0002 +#define AV_SPARC_FSMULD 0x0004 +#define AV_SPARC_V8PLUS 0x0008 +#define AV_SPARC_POPC 0x0010 +#define AV_SPARC_VIS 0x0020 +#define AV_SPARC_VIS2 0x0040 +#define AV_SPARC_ASI_BLK_INIT 0x0080 +#define AV_SPARC_FMAF 0x0100 +#define AV_SPARC_FJFMAU 0x4000 +#define AV_SPARC_IMA 0x8000 + +/* SunOS 5.x hardware capabilities: 386 */ +#define AV_386_FPU 0x00000001 +#define AV_386_TSC 0x00000002 +#define AV_386_CX8 0x00000004 +#define AV_386_SEP 0x00000008 +#define AV_386_AMD_SYSC 0x00000010 +#define AV_386_CMOV 0x00000020 +#define AV_386_MMX 0x00000040 +#define AV_386_AMD_MMX 0x00000080 +#define AV_386_AMD_3DNow 0x00000100 +#define AV_386_AMD_3DNowx 0x00000200 +#define AV_386_FXSR 0x00000400 +#define AV_386_SSE 0x00000800 +#define AV_386_SSE2 0x00001000 +#define AV_386_PAUSE 0x00002000 +#define AV_386_SSE3 0x00004000 +#define AV_386_MON 0x00008000 +#define AV_386_CX16 0x00010000 +#define AV_386_AHF 0x00020000 +#define AV_386_TSCP 0x00040000 +#define AV_386_AMD_SSE4A 0x00080000 +#define AV_386_POPCNT 0x00100000 +#define AV_386_AMD_LZCNT 0x00200000 +#define AV_386_SSSE3 0x00400000 +#define AV_386_SSE4_1 0x00800000 +#define AV_386_SSE4_2 0x01000000 + +#endif diff --git a/contrib/file/src/softmagic.c b/contrib/file/src/softmagic.c new file mode 100644 index 0000000..ed97aa7 --- /dev/null +++ b/contrib/file/src/softmagic.c @@ -0,0 +1,2129 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * softmagic - interpret variable magic from MAGIC + */ + +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: softmagic.c,v 1.191 2014/06/04 17:36:34 christos Exp $") +#endif /* lint */ + +#include "magic.h" +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <time.h> +#if defined(HAVE_LOCALE_H) +#include <locale.h> +#endif + + +private int match(struct magic_set *, struct magic *, uint32_t, + const unsigned char *, size_t, size_t, int, int, int, int, int *, int *, + int *); +private int mget(struct magic_set *, const unsigned char *, + struct magic *, size_t, size_t, unsigned int, int, int, int, int, int *, + int *, int *); +private int magiccheck(struct magic_set *, struct magic *); +private int32_t mprint(struct magic_set *, struct magic *); +private int32_t moffset(struct magic_set *, struct magic *); +private void mdebug(uint32_t, const char *, size_t); +private int mcopy(struct magic_set *, union VALUETYPE *, int, int, + const unsigned char *, uint32_t, size_t, struct magic *); +private int mconvert(struct magic_set *, struct magic *, int); +private int print_sep(struct magic_set *, int); +private int handle_annotation(struct magic_set *, struct magic *); +private void cvt_8(union VALUETYPE *, const struct magic *); +private void cvt_16(union VALUETYPE *, const struct magic *); +private void cvt_32(union VALUETYPE *, const struct magic *); +private void cvt_64(union VALUETYPE *, const struct magic *); + +#define OFFSET_OOB(n, o, i) ((n) < (o) || (i) > ((n) - (o))) +/* + * softmagic - lookup one file in parsed, in-memory copy of database + * Passed the name and FILE * of one file to be typed. + */ +/*ARGSUSED1*/ /* nbytes passed for regularity, maybe need later */ +protected int +file_softmagic(struct magic_set *ms, const unsigned char *buf, size_t nbytes, + size_t level, int mode, int text) +{ + struct mlist *ml; + int rv, printed_something = 0, need_separator = 0; + for (ml = ms->mlist[0]->next; ml != ms->mlist[0]; ml = ml->next) + if ((rv = match(ms, ml->magic, ml->nmagic, buf, nbytes, 0, mode, + text, 0, level, &printed_something, &need_separator, + NULL)) != 0) + return rv; + + return 0; +} + +#define FILE_FMTDEBUG +#ifdef FILE_FMTDEBUG +#define F(a, b, c) file_fmtcheck((a), (b), (c), __FILE__, __LINE__) + +private const char * __attribute__((__format_arg__(3))) +file_fmtcheck(struct magic_set *ms, const struct magic *m, const char *def, + const char *file, size_t line) +{ + const char *ptr = fmtcheck(m->desc, def); + if (ptr == def) + file_magerror(ms, + "%s, %zu: format `%s' does not match with `%s'", + file, line, m->desc, def); + return ptr; +} +#else +#define F(a, b, c) fmtcheck((b)->desc, (c)) +#endif + +/* + * Go through the whole list, stopping if you find a match. Process all + * the continuations of that match before returning. + * + * We support multi-level continuations: + * + * At any time when processing a successful top-level match, there is a + * current continuation level; it represents the level of the last + * successfully matched continuation. + * + * Continuations above that level are skipped as, if we see one, it + * means that the continuation that controls them - i.e, the + * lower-level continuation preceding them - failed to match. + * + * Continuations below that level are processed as, if we see one, + * it means we've finished processing or skipping higher-level + * continuations under the control of a successful or unsuccessful + * lower-level continuation, and are now seeing the next lower-level + * continuation and should process it. The current continuation + * level reverts to the level of the one we're seeing. + * + * Continuations at the current level are processed as, if we see + * one, there's no lower-level continuation that may have failed. + * + * If a continuation matches, we bump the current continuation level + * so that higher-level continuations are processed. + */ +private int +match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, + const unsigned char *s, size_t nbytes, size_t offset, int mode, int text, + int flip, int recursion_level, int *printed_something, int *need_separator, + int *returnval) +{ + uint32_t magindex = 0; + unsigned int cont_level = 0; + int returnvalv = 0, e; /* if a match is found it is set to 1*/ + int firstline = 1; /* a flag to print X\n X\n- X */ + int print = (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) == 0; + + if (returnval == NULL) + returnval = &returnvalv; + + if (file_check_mem(ms, cont_level) == -1) + return -1; + + for (magindex = 0; magindex < nmagic; magindex++) { + int flush = 0; + struct magic *m = &magic[magindex]; + + if (m->type != FILE_NAME) + if ((IS_STRING(m->type) && +#define FLT (STRING_BINTEST | STRING_TEXTTEST) + ((text && (m->str_flags & FLT) == STRING_BINTEST) || + (!text && (m->str_flags & FLT) == STRING_TEXTTEST))) || + (m->flag & mode) != mode) { + /* Skip sub-tests */ + while (magindex + 1 < nmagic && + magic[magindex + 1].cont_level != 0 && + ++magindex) + continue; + continue; /* Skip to next top-level test*/ + } + + ms->offset = m->offset; + ms->line = m->lineno; + + /* if main entry matches, print it... */ + switch (mget(ms, s, m, nbytes, offset, cont_level, mode, text, + flip, recursion_level + 1, printed_something, + need_separator, returnval)) { + case -1: + return -1; + case 0: + flush = m->reln != '!'; + break; + default: + if (m->type == FILE_INDIRECT) + *returnval = 1; + + switch (magiccheck(ms, m)) { + case -1: + return -1; + case 0: + flush++; + break; + default: + flush = 0; + break; + } + break; + } + if (flush) { + /* + * main entry didn't match, + * flush its continuations + */ + while (magindex < nmagic - 1 && + magic[magindex + 1].cont_level != 0) + magindex++; + continue; + } + + if ((e = handle_annotation(ms, m)) != 0) { + *need_separator = 1; + *printed_something = 1; + *returnval = 1; + return e; + } + /* + * If we are going to print something, we'll need to print + * a blank before we print something else. + */ + if (*m->desc) { + *need_separator = 1; + *printed_something = 1; + if (print_sep(ms, firstline) == -1) + return -1; + } + + + if (print && mprint(ms, m) == -1) + return -1; + + ms->c.li[cont_level].off = moffset(ms, m); + + /* and any continuations that match */ + if (file_check_mem(ms, ++cont_level) == -1) + return -1; + + while (++magindex < nmagic && + magic[magindex].cont_level != 0) { + m = &magic[magindex]; + ms->line = m->lineno; /* for messages */ + + if (cont_level < m->cont_level) + continue; + if (cont_level > m->cont_level) { + /* + * We're at the end of the level + * "cont_level" continuations. + */ + cont_level = m->cont_level; + } + ms->offset = m->offset; + if (m->flag & OFFADD) { + ms->offset += + ms->c.li[cont_level - 1].off; + } + +#ifdef ENABLE_CONDITIONALS + if (m->cond == COND_ELSE || + m->cond == COND_ELIF) { + if (ms->c.li[cont_level].last_match == 1) + continue; + } +#endif + switch (mget(ms, s, m, nbytes, offset, cont_level, mode, + text, flip, recursion_level + 1, printed_something, + need_separator, returnval)) { + case -1: + return -1; + case 0: + if (m->reln != '!') + continue; + flush = 1; + break; + default: + if (m->type == FILE_INDIRECT) + *returnval = 1; + flush = 0; + break; + } + + switch (flush ? 1 : magiccheck(ms, m)) { + case -1: + return -1; + case 0: +#ifdef ENABLE_CONDITIONALS + ms->c.li[cont_level].last_match = 0; +#endif + break; + default: +#ifdef ENABLE_CONDITIONALS + ms->c.li[cont_level].last_match = 1; +#endif + if (m->type == FILE_CLEAR) + ms->c.li[cont_level].got_match = 0; + else if (ms->c.li[cont_level].got_match) { + if (m->type == FILE_DEFAULT) + break; + } else + ms->c.li[cont_level].got_match = 1; + if ((e = handle_annotation(ms, m)) != 0) { + *need_separator = 1; + *printed_something = 1; + *returnval = 1; + return e; + } + /* + * If we are going to print something, + * make sure that we have a separator first. + */ + if (*m->desc) { + if (!*printed_something) { + *printed_something = 1; + if (print_sep(ms, firstline) + == -1) + return -1; + } + } + /* + * This continuation matched. Print + * its message, with a blank before it + * if the previous item printed and + * this item isn't empty. + */ + /* space if previous printed */ + if (*need_separator + && ((m->flag & NOSPACE) == 0) + && *m->desc) { + if (print && + file_printf(ms, " ") == -1) + return -1; + *need_separator = 0; + } + if (print && mprint(ms, m) == -1) + return -1; + + ms->c.li[cont_level].off = moffset(ms, m); + + if (*m->desc) + *need_separator = 1; + + /* + * If we see any continuations + * at a higher level, + * process them. + */ + if (file_check_mem(ms, ++cont_level) == -1) + return -1; + break; + } + } + if (*printed_something) { + firstline = 0; + if (print) + *returnval = 1; + } + if ((ms->flags & MAGIC_CONTINUE) == 0 && *printed_something) { + return *returnval; /* don't keep searching */ + } + } + return *returnval; /* This is hit if -k is set or there is no match */ +} + +private int +check_fmt(struct magic_set *ms, struct magic *m) +{ + file_regex_t rx; + int rc, rv = -1; + + if (strchr(m->desc, '%') == NULL) + return 0; + + rc = file_regcomp(&rx, "%[-0-9\\.]*s", REG_EXTENDED|REG_NOSUB); + if (rc) { + file_regerror(&rx, rc, ms); + } else { + rc = file_regexec(&rx, m->desc, 0, 0, 0); + rv = !rc; + } + file_regfree(&rx); + return rv; +} + +#ifndef HAVE_STRNDUP +char * strndup(const char *, size_t); + +char * +strndup(const char *str, size_t n) +{ + size_t len; + char *copy; + + for (len = 0; len < n && str[len]; len++) + continue; + if ((copy = malloc(len + 1)) == NULL) + return NULL; + (void)memcpy(copy, str, len); + copy[len] = '\0'; + return copy; +} +#endif /* HAVE_STRNDUP */ + +private int32_t +mprint(struct magic_set *ms, struct magic *m) +{ + uint64_t v; + float vf; + double vd; + int64_t t = 0; + char buf[128], tbuf[26]; + union VALUETYPE *p = &ms->ms_value; + + switch (m->type) { + case FILE_BYTE: + v = file_signextend(ms, m, (uint64_t)p->b); + switch (check_fmt(ms, m)) { + case -1: + return -1; + case 1: + (void)snprintf(buf, sizeof(buf), "%d", + (unsigned char)v); + if (file_printf(ms, F(ms, m, "%s"), buf) == -1) + return -1; + break; + default: + if (file_printf(ms, F(ms, m, "%d"), + (unsigned char) v) == -1) + return -1; + break; + } + t = ms->offset + sizeof(char); + break; + + case FILE_SHORT: + case FILE_BESHORT: + case FILE_LESHORT: + v = file_signextend(ms, m, (uint64_t)p->h); + switch (check_fmt(ms, m)) { + case -1: + return -1; + case 1: + (void)snprintf(buf, sizeof(buf), "%u", + (unsigned short)v); + if (file_printf(ms, F(ms, m, "%s"), buf) == -1) + return -1; + break; + default: + if (file_printf(ms, F(ms, m, "%u"), + (unsigned short) v) == -1) + return -1; + break; + } + t = ms->offset + sizeof(short); + break; + + case FILE_LONG: + case FILE_BELONG: + case FILE_LELONG: + case FILE_MELONG: + v = file_signextend(ms, m, (uint64_t)p->l); + switch (check_fmt(ms, m)) { + case -1: + return -1; + case 1: + (void)snprintf(buf, sizeof(buf), "%u", (uint32_t) v); + if (file_printf(ms, F(ms, m, "%s"), buf) == -1) + return -1; + break; + default: + if (file_printf(ms, F(ms, m, "%u"), (uint32_t) v) == -1) + return -1; + break; + } + t = ms->offset + sizeof(int32_t); + break; + + case FILE_QUAD: + case FILE_BEQUAD: + case FILE_LEQUAD: + v = file_signextend(ms, m, p->q); + switch (check_fmt(ms, m)) { + case -1: + return -1; + case 1: + (void)snprintf(buf, sizeof(buf), "%" INT64_T_FORMAT "u", + (unsigned long long)v); + if (file_printf(ms, F(ms, m, "%s"), buf) == -1) + return -1; + break; + default: + if (file_printf(ms, F(ms, m, "%" INT64_T_FORMAT "u"), + (unsigned long long) v) == -1) + return -1; + break; + } + t = ms->offset + sizeof(int64_t); + break; + + case FILE_STRING: + case FILE_PSTRING: + case FILE_BESTRING16: + case FILE_LESTRING16: + if (m->reln == '=' || m->reln == '!') { + if (file_printf(ms, F(ms, m, "%s"), m->value.s) == -1) + return -1; + t = ms->offset + m->vallen; + } + else { + char *str = p->s; + + /* compute t before we mangle the string? */ + t = ms->offset + strlen(str); + + if (*m->value.s == '\0') + str[strcspn(str, "\n")] = '\0'; + + if (m->str_flags & STRING_TRIM) { + char *last; + while (isspace((unsigned char)*str)) + str++; + last = str; + while (*last) + last++; + --last; + while (isspace((unsigned char)*last)) + last--; + *++last = '\0'; + } + + if (file_printf(ms, F(ms, m, "%s"), str) == -1) + return -1; + + if (m->type == FILE_PSTRING) + t += file_pstring_length_size(m); + } + break; + + case FILE_DATE: + case FILE_BEDATE: + case FILE_LEDATE: + case FILE_MEDATE: + if (file_printf(ms, F(ms, m, "%s"), + file_fmttime(p->l + m->num_mask, FILE_T_LOCAL, tbuf)) == -1) + return -1; + t = ms->offset + sizeof(uint32_t); + break; + + case FILE_LDATE: + case FILE_BELDATE: + case FILE_LELDATE: + case FILE_MELDATE: + if (file_printf(ms, F(ms, m, "%s"), + file_fmttime(p->l + m->num_mask, 0, tbuf)) == -1) + return -1; + t = ms->offset + sizeof(uint32_t); + break; + + case FILE_QDATE: + case FILE_BEQDATE: + case FILE_LEQDATE: + if (file_printf(ms, F(ms, m, "%s"), + file_fmttime(p->q + m->num_mask, FILE_T_LOCAL, tbuf)) == -1) + return -1; + t = ms->offset + sizeof(uint64_t); + break; + + case FILE_QLDATE: + case FILE_BEQLDATE: + case FILE_LEQLDATE: + if (file_printf(ms, F(ms, m, "%s"), + file_fmttime(p->q + m->num_mask, 0, tbuf)) == -1) + return -1; + t = ms->offset + sizeof(uint64_t); + break; + + case FILE_QWDATE: + case FILE_BEQWDATE: + case FILE_LEQWDATE: + if (file_printf(ms, F(ms, m, "%s"), + file_fmttime(p->q + m->num_mask, FILE_T_WINDOWS, tbuf)) == -1) + return -1; + t = ms->offset + sizeof(uint64_t); + break; + + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + vf = p->f; + switch (check_fmt(ms, m)) { + case -1: + return -1; + case 1: + (void)snprintf(buf, sizeof(buf), "%g", vf); + if (file_printf(ms, F(ms, m, "%s"), buf) == -1) + return -1; + break; + default: + if (file_printf(ms, F(ms, m, "%g"), vf) == -1) + return -1; + break; + } + t = ms->offset + sizeof(float); + break; + + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + vd = p->d; + switch (check_fmt(ms, m)) { + case -1: + return -1; + case 1: + (void)snprintf(buf, sizeof(buf), "%g", vd); + if (file_printf(ms, F(ms, m, "%s"), buf) == -1) + return -1; + break; + default: + if (file_printf(ms, F(ms, m, "%g"), vd) == -1) + return -1; + break; + } + t = ms->offset + sizeof(double); + break; + + case FILE_REGEX: { + char *cp; + int rval; + + cp = strndup((const char *)ms->search.s, ms->search.rm_len); + if (cp == NULL) { + file_oomem(ms, ms->search.rm_len); + return -1; + } + rval = file_printf(ms, F(ms, m, "%s"), cp); + free(cp); + + if (rval == -1) + return -1; + + if ((m->str_flags & REGEX_OFFSET_START)) + t = ms->search.offset; + else + t = ms->search.offset + ms->search.rm_len; + break; + } + + case FILE_SEARCH: + if (file_printf(ms, F(ms, m, "%s"), m->value.s) == -1) + return -1; + if ((m->str_flags & REGEX_OFFSET_START)) + t = ms->search.offset; + else + t = ms->search.offset + m->vallen; + break; + + case FILE_DEFAULT: + case FILE_CLEAR: + if (file_printf(ms, "%s", m->desc) == -1) + return -1; + t = ms->offset; + break; + + case FILE_INDIRECT: + case FILE_USE: + case FILE_NAME: + t = ms->offset; + break; + + default: + file_magerror(ms, "invalid m->type (%d) in mprint()", m->type); + return -1; + } + return (int32_t)t; +} + +private int32_t +moffset(struct magic_set *ms, struct magic *m) +{ + switch (m->type) { + case FILE_BYTE: + return CAST(int32_t, (ms->offset + sizeof(char))); + + case FILE_SHORT: + case FILE_BESHORT: + case FILE_LESHORT: + return CAST(int32_t, (ms->offset + sizeof(short))); + + case FILE_LONG: + case FILE_BELONG: + case FILE_LELONG: + case FILE_MELONG: + return CAST(int32_t, (ms->offset + sizeof(int32_t))); + + case FILE_QUAD: + case FILE_BEQUAD: + case FILE_LEQUAD: + return CAST(int32_t, (ms->offset + sizeof(int64_t))); + + case FILE_STRING: + case FILE_PSTRING: + case FILE_BESTRING16: + case FILE_LESTRING16: + if (m->reln == '=' || m->reln == '!') + return ms->offset + m->vallen; + else { + union VALUETYPE *p = &ms->ms_value; + uint32_t t; + + if (*m->value.s == '\0') + p->s[strcspn(p->s, "\n")] = '\0'; + t = CAST(uint32_t, (ms->offset + strlen(p->s))); + if (m->type == FILE_PSTRING) + t += (uint32_t)file_pstring_length_size(m); + return t; + } + + case FILE_DATE: + case FILE_BEDATE: + case FILE_LEDATE: + case FILE_MEDATE: + return CAST(int32_t, (ms->offset + sizeof(uint32_t))); + + case FILE_LDATE: + case FILE_BELDATE: + case FILE_LELDATE: + case FILE_MELDATE: + return CAST(int32_t, (ms->offset + sizeof(uint32_t))); + + case FILE_QDATE: + case FILE_BEQDATE: + case FILE_LEQDATE: + return CAST(int32_t, (ms->offset + sizeof(uint64_t))); + + case FILE_QLDATE: + case FILE_BEQLDATE: + case FILE_LEQLDATE: + return CAST(int32_t, (ms->offset + sizeof(uint64_t))); + + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + return CAST(int32_t, (ms->offset + sizeof(float))); + + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + return CAST(int32_t, (ms->offset + sizeof(double))); + + case FILE_REGEX: + if ((m->str_flags & REGEX_OFFSET_START) != 0) + return CAST(int32_t, ms->search.offset); + else + return CAST(int32_t, (ms->search.offset + + ms->search.rm_len)); + + case FILE_SEARCH: + if ((m->str_flags & REGEX_OFFSET_START) != 0) + return CAST(int32_t, ms->search.offset); + else + return CAST(int32_t, (ms->search.offset + m->vallen)); + + case FILE_CLEAR: + case FILE_DEFAULT: + case FILE_INDIRECT: + return ms->offset; + + default: + return 0; + } +} + +private int +cvt_flip(int type, int flip) +{ + if (flip == 0) + return type; + switch (type) { + case FILE_BESHORT: + return FILE_LESHORT; + case FILE_BELONG: + return FILE_LELONG; + case FILE_BEDATE: + return FILE_LEDATE; + case FILE_BELDATE: + return FILE_LELDATE; + case FILE_BEQUAD: + return FILE_LEQUAD; + case FILE_BEQDATE: + return FILE_LEQDATE; + case FILE_BEQLDATE: + return FILE_LEQLDATE; + case FILE_BEQWDATE: + return FILE_LEQWDATE; + case FILE_LESHORT: + return FILE_BESHORT; + case FILE_LELONG: + return FILE_BELONG; + case FILE_LEDATE: + return FILE_BEDATE; + case FILE_LELDATE: + return FILE_BELDATE; + case FILE_LEQUAD: + return FILE_BEQUAD; + case FILE_LEQDATE: + return FILE_BEQDATE; + case FILE_LEQLDATE: + return FILE_BEQLDATE; + case FILE_LEQWDATE: + return FILE_BEQWDATE; + case FILE_BEFLOAT: + return FILE_LEFLOAT; + case FILE_LEFLOAT: + return FILE_BEFLOAT; + case FILE_BEDOUBLE: + return FILE_LEDOUBLE; + case FILE_LEDOUBLE: + return FILE_BEDOUBLE; + default: + return type; + } +} +#define DO_CVT(fld, cast) \ + if (m->num_mask) \ + switch (m->mask_op & FILE_OPS_MASK) { \ + case FILE_OPAND: \ + p->fld &= cast m->num_mask; \ + break; \ + case FILE_OPOR: \ + p->fld |= cast m->num_mask; \ + break; \ + case FILE_OPXOR: \ + p->fld ^= cast m->num_mask; \ + break; \ + case FILE_OPADD: \ + p->fld += cast m->num_mask; \ + break; \ + case FILE_OPMINUS: \ + p->fld -= cast m->num_mask; \ + break; \ + case FILE_OPMULTIPLY: \ + p->fld *= cast m->num_mask; \ + break; \ + case FILE_OPDIVIDE: \ + p->fld /= cast m->num_mask; \ + break; \ + case FILE_OPMODULO: \ + p->fld %= cast m->num_mask; \ + break; \ + } \ + if (m->mask_op & FILE_OPINVERSE) \ + p->fld = ~p->fld \ + +private void +cvt_8(union VALUETYPE *p, const struct magic *m) +{ + DO_CVT(b, (uint8_t)); +} + +private void +cvt_16(union VALUETYPE *p, const struct magic *m) +{ + DO_CVT(h, (uint16_t)); +} + +private void +cvt_32(union VALUETYPE *p, const struct magic *m) +{ + DO_CVT(l, (uint32_t)); +} + +private void +cvt_64(union VALUETYPE *p, const struct magic *m) +{ + DO_CVT(q, (uint64_t)); +} + +#define DO_CVT2(fld, cast) \ + if (m->num_mask) \ + switch (m->mask_op & FILE_OPS_MASK) { \ + case FILE_OPADD: \ + p->fld += cast m->num_mask; \ + break; \ + case FILE_OPMINUS: \ + p->fld -= cast m->num_mask; \ + break; \ + case FILE_OPMULTIPLY: \ + p->fld *= cast m->num_mask; \ + break; \ + case FILE_OPDIVIDE: \ + p->fld /= cast m->num_mask; \ + break; \ + } \ + +private void +cvt_float(union VALUETYPE *p, const struct magic *m) +{ + DO_CVT2(f, (float)); +} + +private void +cvt_double(union VALUETYPE *p, const struct magic *m) +{ + DO_CVT2(d, (double)); +} + +/* + * Convert the byte order of the data we are looking at + * While we're here, let's apply the mask operation + * (unless you have a better idea) + */ +private int +mconvert(struct magic_set *ms, struct magic *m, int flip) +{ + union VALUETYPE *p = &ms->ms_value; + uint8_t type; + + switch (type = cvt_flip(m->type, flip)) { + case FILE_BYTE: + cvt_8(p, m); + return 1; + case FILE_SHORT: + cvt_16(p, m); + return 1; + case FILE_LONG: + case FILE_DATE: + case FILE_LDATE: + cvt_32(p, m); + return 1; + case FILE_QUAD: + case FILE_QDATE: + case FILE_QLDATE: + case FILE_QWDATE: + cvt_64(p, m); + return 1; + case FILE_STRING: + case FILE_BESTRING16: + case FILE_LESTRING16: { + /* Null terminate and eat *trailing* return */ + p->s[sizeof(p->s) - 1] = '\0'; + return 1; + } + case FILE_PSTRING: { + size_t sz = file_pstring_length_size(m); + char *ptr1 = p->s, *ptr2 = ptr1 + sz; + size_t len = file_pstring_get_length(m, ptr1); + if (len >= sizeof(p->s)) { + /* + * The size of the pascal string length (sz) + * is 1, 2, or 4. We need at least 1 byte for NUL + * termination, but we've already truncated the + * string by p->s, so we need to deduct sz. + */ + len = sizeof(p->s) - sz; + } + while (len--) + *ptr1++ = *ptr2++; + *ptr1 = '\0'; + return 1; + } + case FILE_BESHORT: + p->h = (short)((p->hs[0]<<8)|(p->hs[1])); + cvt_16(p, m); + return 1; + case FILE_BELONG: + case FILE_BEDATE: + case FILE_BELDATE: + p->l = (int32_t) + ((p->hl[0]<<24)|(p->hl[1]<<16)|(p->hl[2]<<8)|(p->hl[3])); + if (type == FILE_BELONG) + cvt_32(p, m); + return 1; + case FILE_BEQUAD: + case FILE_BEQDATE: + case FILE_BEQLDATE: + case FILE_BEQWDATE: + p->q = (uint64_t) + (((uint64_t)p->hq[0]<<56)|((uint64_t)p->hq[1]<<48)| + ((uint64_t)p->hq[2]<<40)|((uint64_t)p->hq[3]<<32)| + ((uint64_t)p->hq[4]<<24)|((uint64_t)p->hq[5]<<16)| + ((uint64_t)p->hq[6]<<8)|((uint64_t)p->hq[7])); + if (type == FILE_BEQUAD) + cvt_64(p, m); + return 1; + case FILE_LESHORT: + p->h = (short)((p->hs[1]<<8)|(p->hs[0])); + cvt_16(p, m); + return 1; + case FILE_LELONG: + case FILE_LEDATE: + case FILE_LELDATE: + p->l = (int32_t) + ((p->hl[3]<<24)|(p->hl[2]<<16)|(p->hl[1]<<8)|(p->hl[0])); + if (type == FILE_LELONG) + cvt_32(p, m); + return 1; + case FILE_LEQUAD: + case FILE_LEQDATE: + case FILE_LEQLDATE: + case FILE_LEQWDATE: + p->q = (uint64_t) + (((uint64_t)p->hq[7]<<56)|((uint64_t)p->hq[6]<<48)| + ((uint64_t)p->hq[5]<<40)|((uint64_t)p->hq[4]<<32)| + ((uint64_t)p->hq[3]<<24)|((uint64_t)p->hq[2]<<16)| + ((uint64_t)p->hq[1]<<8)|((uint64_t)p->hq[0])); + if (type == FILE_LEQUAD) + cvt_64(p, m); + return 1; + case FILE_MELONG: + case FILE_MEDATE: + case FILE_MELDATE: + p->l = (int32_t) + ((p->hl[1]<<24)|(p->hl[0]<<16)|(p->hl[3]<<8)|(p->hl[2])); + if (type == FILE_MELONG) + cvt_32(p, m); + return 1; + case FILE_FLOAT: + cvt_float(p, m); + return 1; + case FILE_BEFLOAT: + p->l = ((uint32_t)p->hl[0]<<24)|((uint32_t)p->hl[1]<<16)| + ((uint32_t)p->hl[2]<<8) |((uint32_t)p->hl[3]); + cvt_float(p, m); + return 1; + case FILE_LEFLOAT: + p->l = ((uint32_t)p->hl[3]<<24)|((uint32_t)p->hl[2]<<16)| + ((uint32_t)p->hl[1]<<8) |((uint32_t)p->hl[0]); + cvt_float(p, m); + return 1; + case FILE_DOUBLE: + cvt_double(p, m); + return 1; + case FILE_BEDOUBLE: + p->q = ((uint64_t)p->hq[0]<<56)|((uint64_t)p->hq[1]<<48)| + ((uint64_t)p->hq[2]<<40)|((uint64_t)p->hq[3]<<32)| + ((uint64_t)p->hq[4]<<24)|((uint64_t)p->hq[5]<<16)| + ((uint64_t)p->hq[6]<<8) |((uint64_t)p->hq[7]); + cvt_double(p, m); + return 1; + case FILE_LEDOUBLE: + p->q = ((uint64_t)p->hq[7]<<56)|((uint64_t)p->hq[6]<<48)| + ((uint64_t)p->hq[5]<<40)|((uint64_t)p->hq[4]<<32)| + ((uint64_t)p->hq[3]<<24)|((uint64_t)p->hq[2]<<16)| + ((uint64_t)p->hq[1]<<8) |((uint64_t)p->hq[0]); + cvt_double(p, m); + return 1; + case FILE_REGEX: + case FILE_SEARCH: + case FILE_DEFAULT: + case FILE_CLEAR: + case FILE_NAME: + case FILE_USE: + return 1; + default: + file_magerror(ms, "invalid type %d in mconvert()", m->type); + return 0; + } +} + + +private void +mdebug(uint32_t offset, const char *str, size_t len) +{ + (void) fprintf(stderr, "mget/%zu @%d: ", len, offset); + file_showstr(stderr, str, len); + (void) fputc('\n', stderr); + (void) fputc('\n', stderr); +} + +private int +mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, + const unsigned char *s, uint32_t offset, size_t nbytes, struct magic *m) +{ + /* + * Note: FILE_SEARCH and FILE_REGEX do not actually copy + * anything, but setup pointers into the source + */ + if (indir == 0) { + switch (type) { + case FILE_SEARCH: + ms->search.s = RCAST(const char *, s) + offset; + ms->search.s_len = nbytes - offset; + ms->search.offset = offset; + return 0; + + case FILE_REGEX: { + const char *b; + const char *c; + const char *last; /* end of search region */ + const char *buf; /* start of search region */ + const char *end; + size_t lines, linecnt, bytecnt; + + if (s == NULL) { + ms->search.s_len = 0; + ms->search.s = NULL; + return 0; + } + + if (m->str_flags & REGEX_LINE_COUNT) { + linecnt = m->str_range; + bytecnt = linecnt * 80; + } else { + linecnt = 0; + bytecnt = m->str_range; + } + + if (bytecnt == 0) + bytecnt = 8192; + if (bytecnt > nbytes) + bytecnt = nbytes; + + buf = RCAST(const char *, s) + offset; + end = last = RCAST(const char *, s) + bytecnt; + /* mget() guarantees buf <= last */ + for (lines = linecnt, b = buf; lines && b < end && + ((b = CAST(const char *, + memchr(c = b, '\n', CAST(size_t, (end - b))))) + || (b = CAST(const char *, + memchr(c, '\r', CAST(size_t, (end - c)))))); + lines--, b++) { + last = b; + if (b[0] == '\r' && b[1] == '\n') + b++; + } + if (lines) + last = RCAST(const char *, s) + bytecnt; + + ms->search.s = buf; + ms->search.s_len = last - buf; + ms->search.offset = offset; + ms->search.rm_len = 0; + return 0; + } + case FILE_BESTRING16: + case FILE_LESTRING16: { + const unsigned char *src = s + offset; + const unsigned char *esrc = s + nbytes; + char *dst = p->s; + char *edst = &p->s[sizeof(p->s) - 1]; + + if (type == FILE_BESTRING16) + src++; + + /* check that offset is within range */ + if (offset >= nbytes) + break; + for (/*EMPTY*/; src < esrc; src += 2, dst++) { + if (dst < edst) + *dst = *src; + else + break; + if (*dst == '\0') { + if (type == FILE_BESTRING16 ? + *(src - 1) != '\0' : + *(src + 1) != '\0') + *dst = ' '; + } + } + *edst = '\0'; + return 0; + } + case FILE_STRING: /* XXX - these two should not need */ + case FILE_PSTRING: /* to copy anything, but do anyway. */ + default: + break; + } + } + + if (offset >= nbytes) { + (void)memset(p, '\0', sizeof(*p)); + return 0; + } + if (nbytes - offset < sizeof(*p)) + nbytes = nbytes - offset; + else + nbytes = sizeof(*p); + + (void)memcpy(p, s + offset, nbytes); + + /* + * the usefulness of padding with zeroes eludes me, it + * might even cause problems + */ + if (nbytes < sizeof(*p)) + (void)memset(((char *)(void *)p) + nbytes, '\0', + sizeof(*p) - nbytes); + return 0; +} + +private int +mget(struct magic_set *ms, const unsigned char *s, struct magic *m, + size_t nbytes, size_t o, unsigned int cont_level, int mode, int text, + int flip, int recursion_level, int *printed_something, + int *need_separator, int *returnval) +{ + uint32_t soffset, offset = ms->offset; + uint32_t lhs; + int rv, oneed_separator, in_type; + char *sbuf, *rbuf; + union VALUETYPE *p = &ms->ms_value; + struct mlist ml; + + if (recursion_level >= 20) { + file_error(ms, 0, "recursion nesting exceeded"); + return -1; + } + + if (mcopy(ms, p, m->type, m->flag & INDIR, s, (uint32_t)(offset + o), + (uint32_t)nbytes, m) == -1) + return -1; + + if ((ms->flags & MAGIC_DEBUG) != 0) { + fprintf(stderr, "mget(type=%d, flag=%x, offset=%u, o=%zu, " + "nbytes=%zu)\n", m->type, m->flag, offset, o, nbytes); + mdebug(offset, (char *)(void *)p, sizeof(union VALUETYPE)); +#ifndef COMPILE_ONLY + file_mdump(m); +#endif + } + + if (m->flag & INDIR) { + int off = m->in_offset; + if (m->in_op & FILE_OPINDIRECT) { + const union VALUETYPE *q = CAST(const union VALUETYPE *, + ((const void *)(s + offset + off))); + switch (cvt_flip(m->in_type, flip)) { + case FILE_BYTE: + off = q->b; + break; + case FILE_SHORT: + off = q->h; + break; + case FILE_BESHORT: + off = (short)((q->hs[0]<<8)|(q->hs[1])); + break; + case FILE_LESHORT: + off = (short)((q->hs[1]<<8)|(q->hs[0])); + break; + case FILE_LONG: + off = q->l; + break; + case FILE_BELONG: + case FILE_BEID3: + off = (int32_t)((q->hl[0]<<24)|(q->hl[1]<<16)| + (q->hl[2]<<8)|(q->hl[3])); + break; + case FILE_LEID3: + case FILE_LELONG: + off = (int32_t)((q->hl[3]<<24)|(q->hl[2]<<16)| + (q->hl[1]<<8)|(q->hl[0])); + break; + case FILE_MELONG: + off = (int32_t)((q->hl[1]<<24)|(q->hl[0]<<16)| + (q->hl[3]<<8)|(q->hl[2])); + break; + } + if ((ms->flags & MAGIC_DEBUG) != 0) + fprintf(stderr, "indirect offs=%u\n", off); + } + switch (in_type = cvt_flip(m->in_type, flip)) { + case FILE_BYTE: + if (OFFSET_OOB(nbytes, offset, 1)) + return 0; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = p->b & off; + break; + case FILE_OPOR: + offset = p->b | off; + break; + case FILE_OPXOR: + offset = p->b ^ off; + break; + case FILE_OPADD: + offset = p->b + off; + break; + case FILE_OPMINUS: + offset = p->b - off; + break; + case FILE_OPMULTIPLY: + offset = p->b * off; + break; + case FILE_OPDIVIDE: + offset = p->b / off; + break; + case FILE_OPMODULO: + offset = p->b % off; + break; + } + } else + offset = p->b; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + break; + case FILE_BESHORT: + if (OFFSET_OOB(nbytes, offset, 2)) + return 0; + lhs = (p->hs[0] << 8) | p->hs[1]; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = lhs & off; + break; + case FILE_OPOR: + offset = lhs | off; + break; + case FILE_OPXOR: + offset = lhs ^ off; + break; + case FILE_OPADD: + offset = lhs + off; + break; + case FILE_OPMINUS: + offset = lhs - off; + break; + case FILE_OPMULTIPLY: + offset = lhs * off; + break; + case FILE_OPDIVIDE: + offset = lhs / off; + break; + case FILE_OPMODULO: + offset = lhs % off; + break; + } + } else + offset = lhs; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + break; + case FILE_LESHORT: + if (OFFSET_OOB(nbytes, offset, 2)) + return 0; + lhs = (p->hs[1] << 8) | p->hs[0]; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = lhs & off; + break; + case FILE_OPOR: + offset = lhs | off; + break; + case FILE_OPXOR: + offset = lhs ^ off; + break; + case FILE_OPADD: + offset = lhs + off; + break; + case FILE_OPMINUS: + offset = lhs - off; + break; + case FILE_OPMULTIPLY: + offset = lhs * off; + break; + case FILE_OPDIVIDE: + offset = lhs / off; + break; + case FILE_OPMODULO: + offset = lhs % off; + break; + } + } else + offset = lhs; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + break; + case FILE_SHORT: + if (OFFSET_OOB(nbytes, offset, 2)) + return 0; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = p->h & off; + break; + case FILE_OPOR: + offset = p->h | off; + break; + case FILE_OPXOR: + offset = p->h ^ off; + break; + case FILE_OPADD: + offset = p->h + off; + break; + case FILE_OPMINUS: + offset = p->h - off; + break; + case FILE_OPMULTIPLY: + offset = p->h * off; + break; + case FILE_OPDIVIDE: + offset = p->h / off; + break; + case FILE_OPMODULO: + offset = p->h % off; + break; + } + } + else + offset = p->h; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + break; + case FILE_BELONG: + case FILE_BEID3: + if (OFFSET_OOB(nbytes, offset, 4)) + return 0; + lhs = (p->hl[0] << 24) | (p->hl[1] << 16) | + (p->hl[2] << 8) | p->hl[3]; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = lhs & off; + break; + case FILE_OPOR: + offset = lhs | off; + break; + case FILE_OPXOR: + offset = lhs ^ off; + break; + case FILE_OPADD: + offset = lhs + off; + break; + case FILE_OPMINUS: + offset = lhs - off; + break; + case FILE_OPMULTIPLY: + offset = lhs * off; + break; + case FILE_OPDIVIDE: + offset = lhs / off; + break; + case FILE_OPMODULO: + offset = lhs % off; + break; + } + } else + offset = lhs; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + break; + case FILE_LELONG: + case FILE_LEID3: + if (OFFSET_OOB(nbytes, offset, 4)) + return 0; + lhs = (p->hl[3] << 24) | (p->hl[2] << 16) | + (p->hl[1] << 8) | p->hl[0]; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = lhs & off; + break; + case FILE_OPOR: + offset = lhs | off; + break; + case FILE_OPXOR: + offset = lhs ^ off; + break; + case FILE_OPADD: + offset = lhs + off; + break; + case FILE_OPMINUS: + offset = lhs - off; + break; + case FILE_OPMULTIPLY: + offset = lhs * off; + break; + case FILE_OPDIVIDE: + offset = lhs / off; + break; + case FILE_OPMODULO: + offset = lhs % off; + break; + } + } else + offset = lhs; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + break; + case FILE_MELONG: + if (OFFSET_OOB(nbytes, offset, 4)) + return 0; + lhs = (p->hl[1] << 24) | (p->hl[0] << 16) | + (p->hl[3] << 8) | p->hl[2]; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = lhs & off; + break; + case FILE_OPOR: + offset = lhs | off; + break; + case FILE_OPXOR: + offset = lhs ^ off; + break; + case FILE_OPADD: + offset = lhs + off; + break; + case FILE_OPMINUS: + offset = lhs - off; + break; + case FILE_OPMULTIPLY: + offset = lhs * off; + break; + case FILE_OPDIVIDE: + offset = lhs / off; + break; + case FILE_OPMODULO: + offset = lhs % off; + break; + } + } else + offset = lhs; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + break; + case FILE_LONG: + if (OFFSET_OOB(nbytes, offset, 4)) + return 0; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = p->l & off; + break; + case FILE_OPOR: + offset = p->l | off; + break; + case FILE_OPXOR: + offset = p->l ^ off; + break; + case FILE_OPADD: + offset = p->l + off; + break; + case FILE_OPMINUS: + offset = p->l - off; + break; + case FILE_OPMULTIPLY: + offset = p->l * off; + break; + case FILE_OPDIVIDE: + offset = p->l / off; + break; + case FILE_OPMODULO: + offset = p->l % off; + break; + } + } else + offset = p->l; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + break; + default: + break; + } + + switch (in_type) { + case FILE_LEID3: + case FILE_BEID3: + offset = ((((offset >> 0) & 0x7f) << 0) | + (((offset >> 8) & 0x7f) << 7) | + (((offset >> 16) & 0x7f) << 14) | + (((offset >> 24) & 0x7f) << 21)) + 10; + break; + default: + break; + } + + if (m->flag & INDIROFFADD) { + offset += ms->c.li[cont_level-1].off; + if (offset == 0) { + if ((ms->flags & MAGIC_DEBUG) != 0) + fprintf(stderr, + "indirect *zero* offset\n"); + return 0; + } + if ((ms->flags & MAGIC_DEBUG) != 0) + fprintf(stderr, "indirect +offs=%u\n", offset); + } + if (mcopy(ms, p, m->type, 0, s, offset, nbytes, m) == -1) + return -1; + ms->offset = offset; + + if ((ms->flags & MAGIC_DEBUG) != 0) { + mdebug(offset, (char *)(void *)p, + sizeof(union VALUETYPE)); +#ifndef COMPILE_ONLY + file_mdump(m); +#endif + } + } + + /* Verify we have enough data to match magic type */ + switch (m->type) { + case FILE_BYTE: + if (OFFSET_OOB(nbytes, offset, 1)) + return 0; + break; + + case FILE_SHORT: + case FILE_BESHORT: + case FILE_LESHORT: + if (OFFSET_OOB(nbytes, offset, 2)) + return 0; + break; + + case FILE_LONG: + case FILE_BELONG: + case FILE_LELONG: + case FILE_MELONG: + case FILE_DATE: + case FILE_BEDATE: + case FILE_LEDATE: + case FILE_MEDATE: + case FILE_LDATE: + case FILE_BELDATE: + case FILE_LELDATE: + case FILE_MELDATE: + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + if (OFFSET_OOB(nbytes, offset, 4)) + return 0; + break; + + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + if (OFFSET_OOB(nbytes, offset, 8)) + return 0; + break; + + case FILE_STRING: + case FILE_PSTRING: + case FILE_SEARCH: + if (OFFSET_OOB(nbytes, offset, m->vallen)) + return 0; + break; + + case FILE_REGEX: + if (nbytes < offset) + return 0; + break; + + case FILE_INDIRECT: + if (offset == 0) + return 0; + if (nbytes < offset) + return 0; + sbuf = ms->o.buf; + soffset = ms->offset; + ms->o.buf = NULL; + ms->offset = 0; + rv = file_softmagic(ms, s + offset, nbytes - offset, + recursion_level, BINTEST, text); + if ((ms->flags & MAGIC_DEBUG) != 0) + fprintf(stderr, "indirect @offs=%u[%d]\n", offset, rv); + rbuf = ms->o.buf; + ms->o.buf = sbuf; + ms->offset = soffset; + if (rv == 1) { + if ((ms->flags & (MAGIC_MIME|MAGIC_APPLE)) == 0 && + file_printf(ms, F(ms, m, "%u"), offset) == -1) { + free(rbuf); + return -1; + } + if (file_printf(ms, "%s", rbuf) == -1) { + free(rbuf); + return -1; + } + } + free(rbuf); + return rv; + + case FILE_USE: + if (nbytes < offset) + return 0; + sbuf = m->value.s; + if (*sbuf == '^') { + sbuf++; + flip = !flip; + } + if (file_magicfind(ms, sbuf, &ml) == -1) { + file_error(ms, 0, "cannot find entry `%s'", sbuf); + return -1; + } + + oneed_separator = *need_separator; + if (m->flag & NOSPACE) + *need_separator = 0; + rv = match(ms, ml.magic, ml.nmagic, s, nbytes, offset + o, + mode, text, flip, recursion_level, printed_something, + need_separator, returnval); + if (rv != 1) + *need_separator = oneed_separator; + return rv; + + case FILE_NAME: + if (file_printf(ms, "%s", m->desc) == -1) + return -1; + return 1; + case FILE_DEFAULT: /* nothing to check */ + case FILE_CLEAR: + default: + break; + } + if (!mconvert(ms, m, flip)) + return 0; + return 1; +} + +private uint64_t +file_strncmp(const char *s1, const char *s2, size_t len, uint32_t flags) +{ + /* + * Convert the source args to unsigned here so that (1) the + * compare will be unsigned as it is in strncmp() and (2) so + * the ctype functions will work correctly without extra + * casting. + */ + const unsigned char *a = (const unsigned char *)s1; + const unsigned char *b = (const unsigned char *)s2; + uint64_t v; + + /* + * What we want here is v = strncmp(s1, s2, len), + * but ignoring any nulls. + */ + v = 0; + if (0L == flags) { /* normal string: do it fast */ + while (len-- > 0) + if ((v = *b++ - *a++) != '\0') + break; + } + else { /* combine the others */ + while (len-- > 0) { + if ((flags & STRING_IGNORE_LOWERCASE) && + islower(*a)) { + if ((v = tolower(*b++) - *a++) != '\0') + break; + } + else if ((flags & STRING_IGNORE_UPPERCASE) && + isupper(*a)) { + if ((v = toupper(*b++) - *a++) != '\0') + break; + } + else if ((flags & STRING_COMPACT_WHITESPACE) && + isspace(*a)) { + a++; + if (isspace(*b++)) { + if (!isspace(*a)) + while (isspace(*b)) + b++; + } + else { + v = 1; + break; + } + } + else if ((flags & STRING_COMPACT_OPTIONAL_WHITESPACE) && + isspace(*a)) { + a++; + while (isspace(*b)) + b++; + } + else { + if ((v = *b++ - *a++) != '\0') + break; + } + } + } + return v; +} + +private uint64_t +file_strncmp16(const char *a, const char *b, size_t len, uint32_t flags) +{ + /* + * XXX - The 16-bit string compare probably needs to be done + * differently, especially if the flags are to be supported. + * At the moment, I am unsure. + */ + flags = 0; + return file_strncmp(a, b, len, flags); +} + +private int +magiccheck(struct magic_set *ms, struct magic *m) +{ + uint64_t l = m->value.q; + uint64_t v; + float fl, fv; + double dl, dv; + int matched; + union VALUETYPE *p = &ms->ms_value; + + switch (m->type) { + case FILE_BYTE: + v = p->b; + break; + + case FILE_SHORT: + case FILE_BESHORT: + case FILE_LESHORT: + v = p->h; + break; + + case FILE_LONG: + case FILE_BELONG: + case FILE_LELONG: + case FILE_MELONG: + case FILE_DATE: + case FILE_BEDATE: + case FILE_LEDATE: + case FILE_MEDATE: + case FILE_LDATE: + case FILE_BELDATE: + case FILE_LELDATE: + case FILE_MELDATE: + v = p->l; + break; + + case FILE_QUAD: + case FILE_LEQUAD: + case FILE_BEQUAD: + case FILE_QDATE: + case FILE_BEQDATE: + case FILE_LEQDATE: + case FILE_QLDATE: + case FILE_BEQLDATE: + case FILE_LEQLDATE: + case FILE_QWDATE: + case FILE_BEQWDATE: + case FILE_LEQWDATE: + v = p->q; + break; + + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + fl = m->value.f; + fv = p->f; + switch (m->reln) { + case 'x': + matched = 1; + break; + + case '!': + matched = fv != fl; + break; + + case '=': + matched = fv == fl; + break; + + case '>': + matched = fv > fl; + break; + + case '<': + matched = fv < fl; + break; + + default: + file_magerror(ms, "cannot happen with float: invalid relation `%c'", + m->reln); + return -1; + } + return matched; + + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + dl = m->value.d; + dv = p->d; + switch (m->reln) { + case 'x': + matched = 1; + break; + + case '!': + matched = dv != dl; + break; + + case '=': + matched = dv == dl; + break; + + case '>': + matched = dv > dl; + break; + + case '<': + matched = dv < dl; + break; + + default: + file_magerror(ms, "cannot happen with double: invalid relation `%c'", m->reln); + return -1; + } + return matched; + + case FILE_DEFAULT: + case FILE_CLEAR: + l = 0; + v = 0; + break; + + case FILE_STRING: + case FILE_PSTRING: + l = 0; + v = file_strncmp(m->value.s, p->s, (size_t)m->vallen, m->str_flags); + break; + + case FILE_BESTRING16: + case FILE_LESTRING16: + l = 0; + v = file_strncmp16(m->value.s, p->s, (size_t)m->vallen, m->str_flags); + break; + + case FILE_SEARCH: { /* search ms->search.s for the string m->value.s */ + size_t slen; + size_t idx; + + if (ms->search.s == NULL) + return 0; + + slen = MIN(m->vallen, sizeof(m->value.s)); + l = 0; + v = 0; + + for (idx = 0; m->str_range == 0 || idx < m->str_range; idx++) { + if (slen + idx > ms->search.s_len) + break; + + v = file_strncmp(m->value.s, ms->search.s + idx, slen, + m->str_flags); + if (v == 0) { /* found match */ + ms->search.offset += idx; + break; + } + } + break; + } + case FILE_REGEX: { + int rc; + file_regex_t rx; + + if (ms->search.s == NULL) + return 0; + + l = 0; + rc = file_regcomp(&rx, m->value.s, + REG_EXTENDED|REG_NEWLINE| + ((m->str_flags & STRING_IGNORE_CASE) ? REG_ICASE : 0)); + if (rc) { + file_regerror(&rx, rc, ms); + v = (uint64_t)-1; + } else { + regmatch_t pmatch[1]; + size_t slen = ms->search.s_len; +#ifndef REG_STARTEND +#define REG_STARTEND 0 + char c; + if (slen != 0) + slen--; + c = ms->search.s[slen]; + ((char *)(intptr_t)ms->search.s)[slen] = '\0'; +#else + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = slen; +#endif + rc = file_regexec(&rx, (const char *)ms->search.s, + 1, pmatch, REG_STARTEND); +#if REG_STARTEND == 0 + ((char *)(intptr_t)ms->search.s)[l] = c; +#endif + switch (rc) { + case 0: + ms->search.s += (int)pmatch[0].rm_so; + ms->search.offset += (size_t)pmatch[0].rm_so; + ms->search.rm_len = + (size_t)(pmatch[0].rm_eo - pmatch[0].rm_so); + v = 0; + break; + + case REG_NOMATCH: + v = 1; + break; + + default: + file_regerror(&rx, rc, ms); + v = (uint64_t)-1; + break; + } + } + file_regfree(&rx); + if (v == (uint64_t)-1) + return -1; + break; + } + case FILE_INDIRECT: + case FILE_USE: + case FILE_NAME: + return 1; + default: + file_magerror(ms, "invalid type %d in magiccheck()", m->type); + return -1; + } + + v = file_signextend(ms, m, v); + + switch (m->reln) { + case 'x': + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "%" INT64_T_FORMAT + "u == *any* = 1\n", (unsigned long long)v); + matched = 1; + break; + + case '!': + matched = v != l; + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "%" INT64_T_FORMAT "u != %" + INT64_T_FORMAT "u = %d\n", (unsigned long long)v, + (unsigned long long)l, matched); + break; + + case '=': + matched = v == l; + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "%" INT64_T_FORMAT "u == %" + INT64_T_FORMAT "u = %d\n", (unsigned long long)v, + (unsigned long long)l, matched); + break; + + case '>': + if (m->flag & UNSIGNED) { + matched = v > l; + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "%" INT64_T_FORMAT + "u > %" INT64_T_FORMAT "u = %d\n", + (unsigned long long)v, + (unsigned long long)l, matched); + } + else { + matched = (int64_t) v > (int64_t) l; + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "%" INT64_T_FORMAT + "d > %" INT64_T_FORMAT "d = %d\n", + (long long)v, (long long)l, matched); + } + break; + + case '<': + if (m->flag & UNSIGNED) { + matched = v < l; + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "%" INT64_T_FORMAT + "u < %" INT64_T_FORMAT "u = %d\n", + (unsigned long long)v, + (unsigned long long)l, matched); + } + else { + matched = (int64_t) v < (int64_t) l; + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "%" INT64_T_FORMAT + "d < %" INT64_T_FORMAT "d = %d\n", + (long long)v, (long long)l, matched); + } + break; + + case '&': + matched = (v & l) == l; + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "((%" INT64_T_FORMAT "x & %" + INT64_T_FORMAT "x) == %" INT64_T_FORMAT + "x) = %d\n", (unsigned long long)v, + (unsigned long long)l, (unsigned long long)l, + matched); + break; + + case '^': + matched = (v & l) != l; + if ((ms->flags & MAGIC_DEBUG) != 0) + (void) fprintf(stderr, "((%" INT64_T_FORMAT "x & %" + INT64_T_FORMAT "x) != %" INT64_T_FORMAT + "x) = %d\n", (unsigned long long)v, + (unsigned long long)l, (unsigned long long)l, + matched); + break; + + default: + file_magerror(ms, "cannot happen: invalid relation `%c'", + m->reln); + return -1; + } + + return matched; +} + +private int +handle_annotation(struct magic_set *ms, struct magic *m) +{ + if (ms->flags & MAGIC_APPLE) { + if (file_printf(ms, "%.8s", m->apple) == -1) + return -1; + return 1; + } + if ((ms->flags & MAGIC_MIME_TYPE) && m->mimetype[0]) { + if (file_printf(ms, "%s", m->mimetype) == -1) + return -1; + return 1; + } + return 0; +} + +private int +print_sep(struct magic_set *ms, int firstline) +{ + if (ms->flags & MAGIC_MIME) + return 0; + if (firstline) + return 0; + /* + * we found another match + * put a newline and '-' to do some simple formatting + */ + return file_printf(ms, "\n- "); +} diff --git a/contrib/file/src/strcasestr.c b/contrib/file/src/strcasestr.c new file mode 100644 index 0000000..3db407f --- /dev/null +++ b/contrib/file/src/strcasestr.c @@ -0,0 +1,84 @@ +/* $NetBSD: strcasestr.c,v 1.3 2005/11/29 03:12:00 christos Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: strcasestr.c,v 1.3 2005/11/29 03:12:00 christos Exp $"); +__RCSID("$NetBSD: strncasecmp.c,v 1.2 2007/06/04 18:19:27 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include "file.h" + +#include <assert.h> +#include <ctype.h> +#include <string.h> + +static int +_strncasecmp(const char *s1, const char *s2, size_t n) +{ + if (n != 0) { + const unsigned char *us1 = (const unsigned char *)s1, + *us2 = (const unsigned char *)s2; + + do { + if (tolower(*us1) != tolower(*us2++)) + return tolower(*us1) - tolower(*--us2); + if (*us1++ == '\0') + break; + } while (--n != 0); + } + return 0; +} + +/* + * Find the first occurrence of find in s, ignore case. + */ +char * +strcasestr(const char *s, const char *find) +{ + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + c = tolower((unsigned char)c); + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while ((char)tolower((unsigned char)sc) != c); + } while (_strncasecmp(s, find, len) != 0); + s--; + } + return (char *)(intptr_t)(s); +} diff --git a/contrib/file/src/strlcat.c b/contrib/file/src/strlcat.c new file mode 100644 index 0000000..9692bc1 --- /dev/null +++ b/contrib/file/src/strlcat.c @@ -0,0 +1,58 @@ +/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ +#include "file.h" + +#include <sys/types.h> +#include <string.h> + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/contrib/file/src/strlcpy.c b/contrib/file/src/strlcpy.c new file mode 100644 index 0000000..992501c --- /dev/null +++ b/contrib/file/src/strlcpy.c @@ -0,0 +1,54 @@ +/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ +#include "file.h" + +#include <sys/types.h> +#include <string.h> + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/contrib/file/src/tar.h b/contrib/file/src/tar.h new file mode 100644 index 0000000..854d4552 --- /dev/null +++ b/contrib/file/src/tar.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Header file for public domain tar (tape archive) program. + * + * @(#)tar.h 1.20 86/10/29 Public Domain. + * + * Created 25 August 1985 by John Gilmore, ihnp4!hoptoad!gnu. + * + * $File: tar.h,v 1.13 2010/11/30 14:58:53 rrt Exp $ # checkin only + */ + +/* + * Header block on tape. + * + * I'm going to use traditional DP naming conventions here. + * A "block" is a big chunk of stuff that we do I/O on. + * A "record" is a piece of info that we care about. + * Typically many "record"s fit into a "block". + */ +#define RECORDSIZE 512 +#define NAMSIZ 100 +#define TUNMLEN 32 +#define TGNMLEN 32 + +union record { + unsigned char charptr[RECORDSIZE]; + struct header { + char name[NAMSIZ]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char linkflag; + char linkname[NAMSIZ]; + char magic[8]; + char uname[TUNMLEN]; + char gname[TGNMLEN]; + char devmajor[8]; + char devminor[8]; + } header; +}; + +/* The magic field is filled with this if uname and gname are valid. */ +#define TMAGIC "ustar" /* 5 chars and a null */ +#define GNUTMAGIC "ustar " /* 7 chars and a null */ diff --git a/contrib/file/src/vasprintf.c b/contrib/file/src/vasprintf.c new file mode 100644 index 0000000..cc43ae3 --- /dev/null +++ b/contrib/file/src/vasprintf.c @@ -0,0 +1,653 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/*########################################################################### + # # + # vasprintf # + # # + # Copyright (c) 2002-2005 David TAILLANDIER # + # # + ###########################################################################*/ + +/* + +This software is distributed under the "modified BSD licence". + +This software is also released with GNU license (GPL) in another file (same +source-code, only license differ). + + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. Redistributions in binary +form must reproduce the above copyright notice, this list of conditions and +the following disclaimer in the documentation and/or other materials +provided with the distribution. The name of the author may not be used to +endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +==================== + +Hacked from xnprintf version of 26th February 2005 to provide only +vasprintf by Reuben Thomas <rrt@sc3d.org>. + +==================== + + +'printf' function family use the following format string: + +%[flag][width][.prec][modifier]type + +%% is the escape sequence to print a '%' +% followed by an unknown format will print the characters without +trying to do any interpretation + +flag: none + - # (blank) +width: n 0n * +prec: none .0 .n .* +modifier: F N L h l ll z t ('F' and 'N' are ms-dos/16-bit specific) +type: d i o u x X f e g E G c s p n + + +The function needs to allocate memory to store the full text before to +actually writting it. i.e if you want to fnprintf() 1000 characters, the +functions will allocate 1000 bytes. +This behaviour can be modified: you have to customise the code to flush the +internal buffer (writing to screen or file) when it reach a given size. Then +the buffer can have a shorter length. But what? If you really need to write +HUGE string, don't use printf! +During the process, some other memory is allocated (1024 bytes minimum) +to handle the output of partial sprintf() calls. If you have only 10000 bytes +free in memory, you *may* not be able to nprintf() a 8000 bytes-long text. + +note: if a buffer overflow occurs, exit() is called. This situation should +never appear ... but if you want to be *really* sure, you have to modify the +code to handle those situations (only one place to modify). +A buffer overflow can only occur if your sprintf() do strange things or when +you use strange formats. + +*/ +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: vasprintf.c,v 1.12 2014/05/14 23:09:21 christos Exp $") +#endif /* lint */ + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_STDDEF_H +#include <stddef.h> +#endif + +#define ALLOC_CHUNK 2048 +#define ALLOC_SECURITY_MARGIN 1024 /* big value because some platforms have very big 'G' exponent */ +#if ALLOC_CHUNK < ALLOC_SECURITY_MARGIN +# error !!! ALLOC_CHUNK < ALLOC_SECURITY_MARGIN !!! +#endif +/* note: to have some interest, ALLOC_CHUNK should be much greater than ALLOC_SECURITY_MARGIN */ + +/* + * To save a lot of push/pop, every variable are stored into this + * structure, which is passed among nearly every sub-functions. + */ +typedef struct { + const char * src_string; /* current position into intput string */ + char * buffer_base; /* output buffer */ + char * dest_string; /* current position into output string */ + size_t buffer_len; /* length of output buffer */ + size_t real_len; /* real current length of output text */ + size_t pseudo_len; /* total length of output text if it were not limited in size */ + size_t maxlen; + va_list vargs; /* pointer to current position into vargs */ + char * sprintf_string; + FILE * fprintf_file; +} xprintf_struct; + +/* + * Realloc buffer if needed + * Return value: 0 = ok + * EOF = not enought memory + */ +static int realloc_buff(xprintf_struct *s, size_t len) +{ + char * ptr; + + if (len + ALLOC_SECURITY_MARGIN + s->real_len > s->buffer_len) { + len += s->real_len + ALLOC_CHUNK; + ptr = (char *)realloc((void *)(s->buffer_base), len); + if (ptr == NULL) { + s->buffer_base = NULL; + return EOF; + } + + s->dest_string = ptr + (size_t)(s->dest_string - s->buffer_base); + s->buffer_base = ptr; + s->buffer_len = len; + + (s->buffer_base)[s->buffer_len - 1] = 1; /* overflow marker */ + } + + return 0; +} + +/* + * Prints 'usual' characters up to next '%' + * or up to end of text + */ +static int usual_char(xprintf_struct * s) +{ + size_t len; + + len = strcspn(s->src_string, "%"); /* reachs the next '%' or end of input string */ + /* note: 'len' is never 0 because the presence of '%' */ + /* or end-of-line is checked in the calling function */ + + if (realloc_buff(s,len) == EOF) + return EOF; + + memcpy(s->dest_string, s->src_string, len); + s->src_string += len; + s->dest_string += len; + s->real_len += len; + s->pseudo_len += len; + + return 0; +} + +/* + * Return value: 0 = ok + * EOF = error + */ +static int print_it(xprintf_struct *s, size_t approx_len, + const char *format_string, ...) +{ + va_list varg; + int vsprintf_len; + size_t len; + + if (realloc_buff(s,approx_len) == EOF) + return EOF; + + va_start(varg, format_string); + vsprintf_len = vsprintf(s->dest_string, format_string, varg); + va_end(varg); + + /* Check for overflow */ + assert((s->buffer_base)[s->buffer_len - 1] == 1); + + if (vsprintf_len == EOF) /* must be done *after* overflow-check */ + return EOF; + + s->pseudo_len += vsprintf_len; + len = strlen(s->dest_string); + s->real_len += len; + s->dest_string += len; + + return 0; +} + +/* + * Prints a string (%s) + * We need special handling because: + * a: the length of the string is unknown + * b: when .prec is used, we must not access any extra byte of the + * string (of course, if the original sprintf() does... what the + * hell, not my problem) + * + * Return value: 0 = ok + * EOF = error + */ +static int type_s(xprintf_struct *s, int width, int prec, + const char *format_string, const char *arg_string) +{ + size_t string_len; + + if (arg_string == NULL) + return print_it(s, (size_t)6, "(null)", 0); + + /* hand-made strlen() whitch stops when 'prec' is reached. */ + /* if 'prec' is -1 then it is never reached. */ + string_len = 0; + while (arg_string[string_len] != 0 && (size_t)prec != string_len) + string_len++; + + if (width != -1 && string_len < (size_t)width) + string_len = (size_t)width; + + return print_it(s, string_len, format_string, arg_string); +} + +/* + * Read a serie of digits. Stop when non-digit is found. + * Return value: the value read (between 0 and 32767). + * Note: no checks are made against overflow. If the string contain a big + * number, then the return value won't be what we want (but, in this case, + * the programmer don't know whatr he wants, then no problem). + */ +static int getint(const char **string) +{ + int i = 0; + + while (isdigit((unsigned char)**string) != 0) { + i = i * 10 + (**string - '0'); + (*string)++; + } + + if (i < 0 || i > 32767) + i = 32767; /* if we have i==-10 this is not because the number is */ + /* negative; this is because the number is big */ + return i; +} + +/* + * Read a part of the format string. A part is 'usual characters' (ie "blabla") + * or '%%' escape sequence (to print a single '%') or any combination of + * format specifier (ie "%i" or "%10.2d"). + * After the current part is managed, the function returns to caller with + * everything ready to manage the following part. + * The caller must ensure than the string is not empty, i.e. the first byte + * is not zero. + * + * Return value: 0 = ok + * EOF = error + */ +static int dispatch(xprintf_struct *s) +{ + const char *initial_ptr; + char format_string[24]; /* max length may be something like "% +-#032768.32768Ld" */ + char *format_ptr; + int flag_plus, flag_minus, flag_space, flag_sharp, flag_zero; + int width, prec, modifier, approx_width; + char type; + /* most of those variables are here to rewrite the format string */ + +#define SRCTXT (s->src_string) +#define DESTTXT (s->dest_string) + + /* incoherent format string. Characters after the '%' will be printed with the next call */ +#define INCOHERENT() do {SRCTXT=initial_ptr; return 0;} while (0) /* do/while to avoid */ +#define INCOHERENT_TEST() do {if(*SRCTXT==0) INCOHERENT();} while (0) /* a null statement */ + + /* 'normal' text */ + if (*SRCTXT != '%') + return usual_char(s); + + /* we then have a '%' */ + SRCTXT++; + /* don't check for end-of-string ; this is done later */ + + /* '%%' escape sequence */ + if (*SRCTXT == '%') { + if (realloc_buff(s, (size_t)1) == EOF) /* because we can have "%%%%%%%%..." */ + return EOF; + *DESTTXT = '%'; + DESTTXT++; + SRCTXT++; + (s->real_len)++; + (s->pseudo_len)++; + return 0; + } + + /* '%' managing */ + initial_ptr = SRCTXT; /* save current pointer in case of incorrect */ + /* 'decoding'. Points just after the '%' so the '%' */ + /* won't be printed in any case, as required. */ + + /* flag */ + flag_plus = flag_minus = flag_space = flag_sharp = flag_zero = 0; + + for (;; SRCTXT++) { + if (*SRCTXT == ' ') + flag_space = 1; + else if (*SRCTXT == '+') + flag_plus = 1; + else if (*SRCTXT == '-') + flag_minus = 1; + else if (*SRCTXT == '#') + flag_sharp = 1; + else if (*SRCTXT == '0') + flag_zero = 1; + else + break; + } + + INCOHERENT_TEST(); /* here is the first test for end of string */ + + /* width */ + if (*SRCTXT == '*') { /* width given by next argument */ + SRCTXT++; + width = va_arg(s->vargs, int); + if ((size_t)width > 0x3fffU) /* 'size_t' to check against negative values too */ + width = 0x3fff; + } else if (isdigit((unsigned char)*SRCTXT)) /* width given as ASCII number */ + width = getint(&SRCTXT); + else + width = -1; /* no width specified */ + + INCOHERENT_TEST(); + + /* .prec */ + if (*SRCTXT == '.') { + SRCTXT++; + if (*SRCTXT == '*') { /* .prec given by next argument */ + SRCTXT++; + prec = va_arg(s->vargs, int); + if ((size_t)prec >= 0x3fffU) /* 'size_t' to check against negative values too */ + prec = 0x3fff; + } else { /* .prec given as ASCII number */ + if (isdigit((unsigned char)*SRCTXT) == 0) + INCOHERENT(); + prec = getint(&SRCTXT); + } + INCOHERENT_TEST(); + } else + prec = -1; /* no .prec specified */ + + /* modifier */ + switch (*SRCTXT) { + case 'L': + case 'h': + case 'l': + case 'z': + case 't': + modifier = *SRCTXT; + SRCTXT++; + if (modifier=='l' && *SRCTXT=='l') { + SRCTXT++; + modifier = 'L'; /* 'll' == 'L' long long == long double */ + } /* only for compatibility ; not portable */ + INCOHERENT_TEST(); + break; + default: + modifier = -1; /* no modifier specified */ + break; + } + + /* type */ + type = *SRCTXT; + if (strchr("diouxXfegEGcspn",type) == NULL) + INCOHERENT(); /* unknown type */ + SRCTXT++; + + /* rewrite format-string */ + format_string[0] = '%'; + format_ptr = &(format_string[1]); + + if (flag_plus) { + *format_ptr = '+'; + format_ptr++; + } + if (flag_minus) { + *format_ptr = '-'; + format_ptr++; + } + if (flag_space) { + *format_ptr = ' '; + format_ptr++; + } + if (flag_sharp) { + *format_ptr = '#'; + format_ptr++; + } + if (flag_zero) { + *format_ptr = '0'; + format_ptr++; + } /* '0' *must* be the last one */ + + if (width != -1) { + sprintf(format_ptr, "%i", width); + format_ptr += strlen(format_ptr); + } + + if (prec != -1) { + *format_ptr = '.'; + format_ptr++; + sprintf(format_ptr, "%i", prec); + format_ptr += strlen(format_ptr); + } + + if (modifier != -1) { + if (modifier == 'L' && strchr("diouxX",type) != NULL) { + *format_ptr = 'l'; + format_ptr++; + *format_ptr = 'l'; + format_ptr++; + } else { + *format_ptr = modifier; + format_ptr++; + } + } + + *format_ptr = type; + format_ptr++; + *format_ptr = 0; + + /* vague approximation of minimal length if width or prec are specified */ + approx_width = width + prec; + if (approx_width < 0) /* because width == -1 and/or prec == -1 */ + approx_width = 0; + + switch (type) { + /* int */ + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + switch (modifier) { + case -1 : + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); + case 'L': + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long long int)); + case 'l': + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long int)); + case 'h': + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); + case 'z': + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, size_t)); + case 't': + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, ptrdiff_t)); + /* 'int' instead of 'short int' because default promotion is 'int' */ + default: + INCOHERENT(); + } + + /* char */ + case 'c': + if (modifier != -1) + INCOHERENT(); + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); + /* 'int' instead of 'char' because default promotion is 'int' */ + + /* math */ + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + switch (modifier) { + case -1 : /* because of default promotion, no modifier means 'l' */ + case 'l': + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, double)); + case 'L': + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long double)); + default: + INCOHERENT(); + } + + /* string */ + case 's': + return type_s(s, width, prec, format_string, va_arg(s->vargs, const char*)); + + /* pointer */ + case 'p': + if (modifier == -1) + return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, void *)); + INCOHERENT(); + + /* store */ + case 'n': + if (modifier == -1) { + int * p; + p = va_arg(s->vargs, int *); + if (p != NULL) { + *p = s->pseudo_len; + return 0; + } + return EOF; + } + INCOHERENT(); + + } /* switch */ + + INCOHERENT(); /* unknown type */ + +#undef INCOHERENT +#undef INCOHERENT_TEST +#undef SRCTXT +#undef DESTTXT +} + +/* + * Return value: number of *virtually* written characters + * EOF = error + */ +static int core(xprintf_struct *s) +{ + size_t save_len; + char *dummy_base; + + /* basic checks */ + if ((int)(s->maxlen) <= 0) /* 'int' to check against some conversion */ + return EOF; /* error for example if value is (int)-10 */ + s->maxlen--; /* because initial maxlen counts final 0 */ + /* note: now 'maxlen' _can_ be zero */ + + if (s->src_string == NULL) + s->src_string = "(null)"; + + /* struct init and memory allocation */ + s->buffer_base = NULL; + s->buffer_len = 0; + s->real_len = 0; + s->pseudo_len = 0; + if (realloc_buff(s, (size_t)0) == EOF) + return EOF; + s->dest_string = s->buffer_base; + + /* process source string */ + for (;;) { + /* up to end of source string */ + if (*(s->src_string) == 0) { + *(s->dest_string) = '\0'; /* final NUL */ + break; + } + + if (dispatch(s) == EOF) + goto free_EOF; + + /* up to end of dest string */ + if (s->real_len >= s->maxlen) { + (s->buffer_base)[s->maxlen] = '\0'; /* final NUL */ + break; + } + } + + /* for (v)asnprintf */ + dummy_base = s->buffer_base; + + dummy_base = s->buffer_base + s->real_len; + save_len = s->real_len; + + /* process the remaining of source string to compute 'pseudo_len'. We + * overwrite again and again, starting at 'dummy_base' because we don't + * need the text, only char count. */ + while(*(s->src_string) != 0) { /* up to end of source string */ + s->real_len = 0; + s->dest_string = dummy_base; + if (dispatch(s) == EOF) + goto free_EOF; + } + + s->buffer_base = (char *)realloc((void *)(s->buffer_base), save_len + 1); + if (s->buffer_base == NULL) + return EOF; /* should rarely happen because we shrink the buffer */ + return s->pseudo_len; + + free_EOF: + free(s->buffer_base); + return EOF; +} + +int vasprintf(char **ptr, const char *format_string, va_list vargs) +{ + xprintf_struct s; + int retval; + + s.src_string = format_string; +#ifdef va_copy + va_copy (s.vargs, vargs); +#else +#ifdef __va_copy + __va_copy (s.vargs, vargs); +#else + memcpy (&s.vargs, &vargs, sizeof (s.va_args)); +#endif /* __va_copy */ +#endif /* va_copy */ + s.maxlen = (size_t)INT_MAX; + + retval = core(&s); + va_end(s.vargs); + if (retval == EOF) { + *ptr = NULL; + return EOF; + } + + *ptr = s.buffer_base; + return retval; +} |