diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:06:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:06:04 +0000 |
commit | 2f0649f6fe411d7e07c8d56cf8ea56db53536da8 (patch) | |
tree | 778611fb52176dce1ad06c68e87b2cb348ca0f7b | |
parent | Initial commit. (diff) | |
download | klibc-upstream/2.0.13.tar.xz klibc-upstream/2.0.13.zip |
Adding upstream version 2.0.13.upstream/2.0.13upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
910 files changed, 86136 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a60b89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# +# NOTE! Don't add files that are generated in specific +# subdirectories here. Add them in the ".gitignore" file +# in that subdirectory instead. +# +# Normal rules +# +*.o +*.o.cmd +.*.cmd +*.g +\#* +.\#* +*~ + +# Top-level ignorables +/linux +/.config + +# Common generated subdirectores +shared +static @@ -0,0 +1,18 @@ +# +# Kbuild file for klibc +# +.PHONY: $(obj)/all +always := all + +$(obj)/all: + $(Q)$(MAKE) $(klibc)=scripts/basic + $(Q)$(MAKE) $(klibc)=usr/klibc + $(Q)$(MAKE) $(klibc)=usr/kinit + $(Q)$(MAKE) $(klibc)=usr/dash + $(Q)$(MAKE) $(klibc)=usr/utils + $(Q)$(MAKE) $(klibc)=usr/gzip + + +# Directories to visit during clean and install +subdir- := scripts/basic klcc usr/klibc usr/dash usr/utils usr/gzip \ + usr/kinit usr/klibc/tests diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c99b962 --- /dev/null +++ b/Makefile @@ -0,0 +1,185 @@ +SRCROOT = . + +# *DOCUMENTATION* +# To see a list of typical targets execute "make help" + +# kbuild compatibility +export srctree := $(or $(KBUILD_SRC),$(shell pwd)) +export objtree := $(shell pwd) +export KLIBCSRC := usr/klibc +export VERSION := $(shell cat $(srctree)/$(KLIBCSRC)/version) +export KLIBCINC := usr/include +export KLIBCOBJ := usr/klibc +export KLIBCKERNELSRC ?= linux + +export VPATH := $(srctree) + +include $(srctree)/scripts/Kbuild.include + +KLIBCROSS ?= $(CROSS_COMPILE) +export KLIBCROSS +export CC := $(KLIBCROSS)gcc +export LD := $(KLIBCROSS)ld +export AR := $(KLIBCROSS)ar +export RANLIB := $(KLIBCROSS)ranlib +export STRIP := $(KLIBCROSS)strip +export NM := $(KLIBCROSS)nm +export OBJCOPY := $(KLIBCROSS)objcopy +export OBJDUMP := $(KLIBCROSS)objdump + +NOSTDINC_FLAGS := -nostdlib -nostdinc -isystem $(shell $(CC) -print-file-name=include) + +ARCH := $(shell uname -m | sed -e s/i.86/i386/ \ + -e s/parisc64/parisc/ -e s/sun4u/sparc64/ \ + -e s/arm.*/arm/ -e s/sa110/arm/ \ + -e s/aarch64.*/arm64/ -e s/sh.*/sh/ \ + -e s/ppc64le/ppc64/) +export KLIBCARCH ?= $(ARCH) +export KLIBCARCHDIR := $(shell echo $(KLIBCARCH) | sed -e s/s390x/s390/) + +export HOSTCC := gcc +export HOSTCFLAGS := -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +export PERL := perl + +# Location for installation +export prefix = /usr +export bindir = $(prefix)/bin +export libdir = $(prefix)/lib +export mandir = $(prefix)/man +export INSTALLDIR = $(prefix)/lib/klibc +export INSTALLROOT = + +# Create a fake .config as present in the kernel tree +# But if it exists leave it alone +$(if $(wildcard $(objtree)/.config),,\ + $(shell cp $(srctree)/defconfig $(objtree)/.config)) + +# Prefix Make commands with $(Q) to silence them +# Use quiet_cmd_xxx, cmd_xxx to create nice output +# use make V=1 to get verbose output + +ifdef V + ifeq ("$(origin V)", "command line") + KBUILD_VERBOSE = $(V) + endif +endif +ifndef KBUILD_VERBOSE + KBUILD_VERBOSE = 0 +endif + +ifeq ($(KBUILD_VERBOSE),1) + quiet = + Q = +else + quiet=quiet_ + Q = @ +endif + +# If the user is running make -s (silent mode), suppress echoing of +# commands + +ifneq ($(findstring s,$(MAKEFLAGS)),) + quiet=silent_ +endif + +export quiet Q KBUILD_VERBOSE + +# Do not print "Entering directory ..." +MAKEFLAGS += --no-print-directory + +# Shorthand to call Kbuild.klibc +klibc := -f $(srctree)/scripts/Kbuild.klibc obj + +# Very first target +.PHONY: all klcc klibc +all: klcc klibc + +$(objtree)/.config: $(srctree)/defconfig + @echo "defconfig has changed, please remove or edit .config" + @false + +$(KLIBCKERNELSRC)/include: + @echo 'Missing kernel UAPI headers in $(KLIBCKERNELSRC)/include.' + @echo 'Install them by running:' + @echo ' make headers_install INSTALL_HDR_PATH=$(abspath $(KLIBCKERNELSRC))' + @echo 'in the kernel source directory.' + @false + +rpmbuild = $(shell which rpmbuild 2>/dev/null || which rpm) + +klibc.spec: klibc.spec.in $(KLIBCSRC)/version + sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@ + +# Build klcc - it is the first target +klcc: $(objtree)/.config $(KLIBCKERNELSRC)/include + $(Q)$(MAKE) $(klibc)=klcc + +klibc: $(objtree)/.config $(KLIBCKERNELSRC)/include + $(Q)$(MAKE) $(klibc)=. + +test: klibc + $(Q)$(MAKE) $(klibc)=usr/klibc/tests + +help: + @echo 'Cleaning targets:' + @echo ' clean - Remove most generated files' + @echo ' mrproper - Remove all generated files + config' + @echo ' distclean - mprproper + editor backup + patch files' + @echo '' + @echo 'Build targets:' + @echo 'all - Build all targets' + @echo 'install - Install klibc' + @echo 'klcc - Wrapper around gcc to compile against klibc' + @echo 'test - Run klibc tests' + @echo + @echo 'Build options:' + @echo 'KLIBCKERNELSRC - Path to usr directory containing UAPI headers' + @echo 'make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build' + @echo 'make V=2 [targets] 2 => give reason for rebuild of target' + @echo + @echo 'Sample invocation:' + @echo 'make KLIBCKERNELSRC=`pwd`/../linux/usr/' + +### +# allow one to say make dir/file.o +# Caveat: works only for .c files where we have a Kbuild file in same dir +%.o: %.c FORCE + $(Q)$(MAKE) $(klibc)=$(dir $<) $(dir $<)$(notdir $@) + +%.s: %.c FORCE + $(Q)$(MAKE) $(klibc)=$(dir $<) $(dir $<)$(notdir $@) + +%.i: %.c FORCE + $(Q)$(MAKE) $(klibc)=$(dir $<) $(dir $<)$(notdir $@) + +FORCE: ; +### +# clean: remove generated files +# mrproper does a full cleaning including .config and linux symlink +FIND_IGNORE := \( -name .git -o -name .pc \) -prune -o +quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),RM $(wildcard $(rm-files))) + cmd_rmfiles = rm -f $(rm-files) +clean: + $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.clean obj=. + $(Q)find . $(FIND_IGNORE) \ + \( -name *.o -o -name *.a -o -name '.*.cmd' -o \ + -name '.*.d' -o -name '.*.tmp' \) \ + -type f -print | xargs rm -f + +rm-files := $(objtree)/.config linux +distclean mrproper: clean + $(Q)find . $(FIND_IGNORE) \ + \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ + -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ + -o -name '.*.rej' -o -size 0 \ + -o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \ + -type f -print | xargs rm -f + $(call cmd,rmfiles) + +install: all + $(Q)$(MAKE) -f $(srctree)/scripts/Kbuild.install obj=. + +# This does all the prep work needed to turn a freshly exported git repository +# into a release tarball tree +release: klibc.spec + rm -f .config @@ -0,0 +1,33 @@ +Please see usr/klibc/README.klibc for build instructions and for the status of +various platforms. + + + +klibc is archived at: + + https://mirrors.kernel.org/pub/linux/libs/klibc/ + +There is a mailing list for klibc and early-userspace issues at: + + klibc@zytor.com + + https://www.zytor.com/mailman/listinfo/klibc + +klibc is maintained in the git version control system. The git +repository can be viewed on the web at: + + https://git.kernel.org/pub/scm/libs/klibc/klibc.git/ + +To clone the klibc repository using git: + + git clone https://git.kernel.org/pub/scm/libs/klibc/klibc.git <workdir> + +To update an already cloned tree: + + git pull + +For more information on git, see: + + https://git-scm.com/ + https://mirrors.kernel.org/pub/software/scm/git/docs/gittutorial.html + https://mirrors.kernel.org/pub/software/scm/git/docs/ diff --git a/contrib/klibc.m4 b/contrib/klibc.m4 new file mode 100644 index 0000000..66da30e --- /dev/null +++ b/contrib/klibc.m4 @@ -0,0 +1,93 @@ +# klibc.m4 serial 99
+## Copyright (C) 1995-2003 Free Software Foundation, Inc.
+## This file is free software, distributed under the terms of the GNU
+## General Public License. As a special exception to the GNU General
+## Public License, this file may be distributed as part of a program
+## that contains a configuration script generated by Autoconf, under
+## the same distribution terms as the rest of that program.
+##
+## This file can can be used in projects which are not available under
+## the GNU General Public License or the GNU Library General Public
+## License but which still want to provide support for the GNU gettext
+## functionality.
+## Please note that the actual code of the KLIBC Library is partly covered
+## by the GNU Library General Public License, and party copyrighted by the
+## Regents of The University of California, and the rest is covered by a
+## MIT style license.
+
+# Authors:
+# Martin Schlemmer <azarah@nosferatu.za.org>, 2005.
+
+
+# AC_CHECK_KLIBC
+# --------------
+# Check if the user wants KLIBC support enabled. If so, set KLIBC=yes and
+# fill in KLIBC_PREFIX, KLIBC_BINDIR, KLIBC_SBINDIR, KLIBC_LIBDIR and
+# KLIBC_INCLUDEDIR. CC is also set to the proper klcc executable.
+# NOTE: This should be called before AC_PROG_CC, and before header, function
+# or type checks.
+AC_DEFUN([AC_CHECK_KLIBC],
+[AC_BEFORE([$0], [AC_PROG_CC])
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_ARG_ENABLE([klibc],
+ [AS_HELP_STRING([--enable-klibc],
+ [Enable linking to klibc [no]. You need at
+ least klibc-1.0 or later for this. Set KLCC
+ to the absolute file name of klcc if not in
+ the PATH])],
+ [KLIBC=$enableval], [KLIBC=no])
+AC_ARG_ENABLE([klibc-layout],
+ [AS_HELP_STRING([--enable-klibc-layout],
+ [Enable installing binaries, libraries and
+ headers into the klibc prefix [yes] ])],
+ [if test "X$KLIBC" != Xno; then
+ KLIBC_LAYOUT=$enableval
+ else
+ KLIBC_LAYOUT=no
+ fi],
+ [if test "X$KLIBC" != Xno; then
+ KLIBC_LAYOUT=yes
+ else
+ KLIBC_LAYOUT=no
+ fi])
+
+if test "X$KLIBC" != Xno; then
+ # Basic cross compiling support. I do not think it is wise to use
+ # AC_CHECK_TOOL, because if we are cross compiling, we do not want
+ # just 'klcc' to be returned ...
+ if test "${host_alias}" != "${build_alias}"; then
+ AC_CHECK_PROGS([KLCC], [${host_alias}-klcc], [no])
+ else
+ AC_CHECK_PROGS([KLCC], [klcc], [no])
+ fi
+ if test "X$KLCC" = Xno; then
+ AC_MSG_ERROR([cannot find klibc frontend 'klcc'!])
+ fi
+
+ CC="$KLCC"
+ CFLAGS="-Os"
+
+ KLIBC_KCROSS="$($KLCC -print-klibc-kcross 2>/dev/null)"
+ KLIBC_PREFIX="$($KLCC -print-klibc-prefix 2>/dev/null)"
+ KLIBC_BIN_DIR="$($KLCC -print-klibc-bindir 2>/dev/null)"
+ KLIBC_SBIN_DIR="${KLIBC_PREFIX}/${KLIBC_KCROSS}sbin"
+ KLIBC_LIB_DIR="$($KLCC -print-klibc-libdir 2>/dev/null)"
+ KLIBC_INCLUDE_DIR="$($KLCC -print-klibc-includedir 2>/dev/null)"
+
+ if test "X$KLIBC_LAYOUT" != Xno; then
+ prefix="$KLIBC_PREFIX"
+ bindir="$KLIBC_BIN_DIR"
+ sbindir="$KLIBC_SBIN_DIR"
+ libdir="$KLIBC_LIB_DIR"
+ includedir="$KLIBC_INCLUDE_DIR"
+ fi
+
+ # At least KLIBC_LIB_DIR should be valid, else klibc is too old or
+ # something went wrong
+ if test ! -d "$KLIBC_LIB_DIR"; then
+ AC_MSG_ERROR([your klibc installation is too old or not functional!])
+ fi
+fi
+
+AC_SUBST(KLIBC)
+])# AC_CHECK_KLIBC
diff --git a/defconfig b/defconfig new file mode 100644 index 0000000..7d46de9 --- /dev/null +++ b/defconfig @@ -0,0 +1,10 @@ +CONFIG_KLIBC=y +CONFIG_KLIBC_ERRLIST=y +CONFIG_KLIBC_ZLIB=y +# CONFIG_KLIBC_ZIP is not set +# i386 option +CONFIG_REGPARM=y +# ARM options +# CONFIG_KLIBC_THUMB is not set +CONFIG_AEABI=y +# CONFIG_DEBUG_INFO is not set diff --git a/git-rm-for-kernel.sh b/git-rm-for-kernel.sh new file mode 100755 index 0000000..395692d --- /dev/null +++ b/git-rm-for-kernel.sh @@ -0,0 +1,17 @@ +#!/bin/sh +if [ -z "$RM" ]; then + export RM='git rm -rf' +fi + +nuke () { + find "$@" -print | sort -r | xargs -rt $RM +} + +nuke README Kbuild Makefile defconfig klibc.spec.in *.sh +nuke contrib klcc + +# These files are either not needed or provided from the +# kernel tree +nuke scripts/Kbuild.include scripts/Kbuild.install +nuke scripts/Makefile.* +nuke scripts/basic diff --git a/klcc/.gitignore b/klcc/.gitignore new file mode 100644 index 0000000..b331920 --- /dev/null +++ b/klcc/.gitignore @@ -0,0 +1,2 @@ +klcc +klibc.config diff --git a/klcc/Kbuild b/klcc/Kbuild new file mode 100644 index 0000000..82ca0e8 --- /dev/null +++ b/klcc/Kbuild @@ -0,0 +1,47 @@ +# +# Build klcc +# + +always := $(KLIBCCROSS)klcc + +$(obj)/$(KLIBCCROSS)klibc.config: $(src)/Kbuild \ + $(srctree)/Makefile \ + $(srctree)/scripts/Kbuild.klibc + @$(kecho) ' GEN $@' + $(Q)rm -f $@ + $(Q)echo 'ARCH=$(KLIBCARCH)' >> $@ + $(Q)echo 'ARCHDIR=$(KLIBCARCHDIR)' >> $@ + $(Q)echo 'CROSS=$(KLIBCROSS)' >> $@ + $(Q)echo 'KCROSS=$(KCROSS)' >> $@ + $(Q)echo 'CC=$(KLIBCCC)' >> $@ + $(Q)echo 'LD=$(KLIBCLD)' >> $@ + $(Q)echo 'REQFLAGS=$(filter-out -I%,$(KLIBCDEFS) $(KLIBCREQFLAGS) $(KLIBCARCHREQFLAGS) $(KLIBCCPPFLAGS))' >> $@ + $(Q)echo 'OPTFLAGS=$(KLIBCOPTFLAGS)' >> $@ + $(Q)echo 'LDFLAGS=$(KLIBCLDFLAGS)' >> $@ + $(Q)echo 'STRIP=$(STRIP)' >> $@ + $(Q)echo 'STRIPFLAGS=$(KLIBCSTRIPFLAGS)' >> $@ + $(Q)echo 'EMAIN=$(KLIBCEMAIN)' >> $@ + $(Q)echo 'CRTSHARED=$(notdir $(KLIBCCRTSHARED))' >> $@ + $(Q)echo 'BITSIZE=$(KLIBCBITSIZE)' >> $@ + $(Q)echo 'VERSION=$(shell cat $(srctree)/usr/klibc/version)' >> $@ + $(Q)echo 'prefix=$(INSTALLDIR)' >> $@ + $(Q)echo 'bindir=$(INSTALLDIR)/$(KCROSS)bin' >> $@ + $(Q)echo 'libdir=$(INSTALLDIR)/$(KCROSS)lib' >> $@ + $(Q)echo 'includedir=$(INSTALLDIR)/$(KCROSS)include' >> $@ + + +# Generate klcc +targets := $(KLIBCCROSS)klcc + +quiet_cmd_klcc = GEN $@ + cmd_klcc = $(PERL) $< $(srctree)/$(src)/klcc.in \ + $(obj)/$(KLIBCCROSS)klibc.config \ + $(shell command -v $(PERL) 2>/dev/null) \ + > $@ || ( rm -f $@ ; exit 1 ) && \ + chmod a+x $@ +$(obj)/$(KLIBCCROSS)klcc: $(src)/makeklcc.pl $(src)/klcc.in \ + $(obj)/$(KLIBCCROSS)klibc.config + $(call if_changed,klcc) + +# Cleaning targets +clean-files := $(KLIBCCROSS)klibc.config $(KLIBCCROSS)klcc diff --git a/klcc/klcc.1 b/klcc/klcc.1 new file mode 100644 index 0000000..0186931 --- /dev/null +++ b/klcc/klcc.1 @@ -0,0 +1,118 @@ +.\" $Id: klcc.1,v 1.3 2005/04/19 23:27:46 hpa Exp $ +.\" ----------------------------------------------------------------------- +.\" +.\" Copyright 2005 H. Peter Anvin - All Rights Reserved +.\" +.\" Permission is hereby granted, free of charge, to any person +.\" obtaining a copy of this software and associated documentation +.\" files (the "Software"), to deal in the Software without +.\" restriction, including without limitation the rights to use, +.\" copy, modify, merge, publish, distribute, sublicense, and/or +.\" sell copies of the Software, and to permit persons to whom +.\" the Software is furnished to do so, subject to the following +.\" conditions: +.\" +.\" The above copyright notice and this permission notice shall +.\" be included in all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +.\" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +.\" OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +.\" HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +.\" WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +.\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +.\" OTHER DEALINGS IN THE SOFTWARE. +.\" +.\" ----------------------------------------------------------------------- + +.TH klcc "1" "1 March 2005" "klibc" "H. Peter Anvin" +.SH NAME +klcc \- compile a program against klibc +.SH SYNOPSIS +.B klcc +[\fIgcc options\fP] +[\fB\-o\fP \fIoutfile\fP] +\fIinfile...\fP +.SH DESCRIPTION +.PP +.B klcc +is a wrapper around +.BR gcc (1) +and +.BR ld (1) +which compiles and links a program against the +.B klibc +tiny C library. It supports most +.B gcc +options. +.PP +Unlike +.BR gcc , +.B klcc +compiles with optimization on by default. Furthermore, the +optimization level used depends on whether or not +.B \-g +is specified, since +.B klcc +frequently uses options in the normal case which makes debugging +impossible. Therefore, compile without +.BR \-g , +.BR \-O , +.B \-f +or +.B \-m +option to use the default optimization level; this will generally +result in the smallest binaries. You may want to use +.B \-s +when linking, however. Use +.B \-O0 +to compile without any optimization whatsoever; this may not work depending +on the version of +.B gcc +used. +.TP +.B \-nostdinc +allows to turn off klibc include files. +.PP +Use the +.B \-shared +or +.B \-static +option to compile for and link against shared or static klibc. Note +that shared klibc only supports running against the exact same klibc +binary as the binary was linked with. +.PP +In addition to standard +.B gcc +options, +.B klcc +supports options of the form \fB\-print-klibc-\fP\fIoption\fP, +which prints the corresponding klibc configuration option. +.SH AUTHOR +Written by H. Peter Anvin <hpa@zytor.com>. +.SH COPYRIGHT +Copyright \(co 2005 H. Peter Anvin \- All Rights Reserved +.PP +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following +conditions: +.PP +The above copyright notice and this permission notice shall +be included in all copies or substantial portions of the Software. +.PP +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +.SH "SEE ALSO" +.BR gcc (1) diff --git a/klcc/klcc.in b/klcc/klcc.in new file mode 100644 index 0000000..fab3239 --- /dev/null +++ b/klcc/klcc.in @@ -0,0 +1,277 @@ +# -*- perl -*- + +use IPC::Open3; + +# Standard includes +@includes = ("-I${prefix}/${KCROSS}include/arch/${ARCHDIR}", + "-I${prefix}/${KCROSS}include/bits${BITSIZE}", + "-I${prefix}/${KCROSS}include"); + +# Default optimization options (for compiles without -g) +@optopt = @OPTFLAGS; +@goptopt = ('-O'); + +# Standard library directories +@stdlibpath = ("-L${prefix}/${KCROSS}lib"); + +# Options and libraries to pass to ld; shared versus static +@staticopt = ("${prefix}/${KCROSS}lib/crt0.o"); +@staticlib = ("${prefix}/${KCROSS}lib/libc.a"); +@sharedopt = (@EMAIN, map { "${prefix}/${KCROSS}lib/$_" } @CRTSHARED); +@sharedlib = ('-R', "${prefix}/${KCROSS}lib/libc.so"); + +# Returns the language (-x option string) for a specific extension. +sub filename2lang($) { + my ($file) = @_; + + return 'c' if ( $file =~ /\.c$/ ); + return 'c-header' if ( $file =~ /\.h$/ ); + return 'cpp-output' if ( $file =~ /\.i$/ ); + return 'c++-cpp-output' if ( $file =~ /\.ii$/ ); + return 'objective-c' if ( $file =~ /\.m$/ ); + return 'objc-cpp-output' if ( $file =~ /\.mi$/ ); + return 'c++' if ( $file =~/\.(cc|cp|cxx|cpp|CPP|c\+\+|C)$/ ); + return 'c++-header' if ( $file =~ /\.(hh|H)$/ ); + return 'f77' if ( $file =~ /\.(f|for|FOR)$/ ); + return 'f77-cpp-input' if ( $file =~ /\.(F|fpp|FPP)$/ ); + return 'ratfor' if ( $file =~ /\.r$/ ); + + # Is this correct? + return 'ada' if ( $file =~ /\.(ads|adb)$/ ); + + return 'assembler' if ( $file =~ /\.s$/ ); + return 'assembler-with-cpp' if ( $file =~/\.S$/ ); + + # Linker file; there is no option to gcc to assume something + # is a linker file, so we make up our own... + return 'obj'; +} + +# Produces a series of -x options and files +sub files_with_lang($$) { + my($files, $flang) = @_; + my(@as) = (); + my($xopt) = 'none'; + my($need); + + foreach $f ( @{$files} ) { + $need = ${$flang}{$f}; + + # Skip object files + if ( $need ne 'obj' ) { + unless ( $xopt eq $need || $need eq 'stdin') { + push(@as, '-x', $need); + $xopt = $need; + } + push(@as, $f); + } + } + + return @as; +} + +# Convert a return value from system() to an exit() code +sub syserr($) { + my($e) = @_; + + return ($e & 0x7f) | 0x80 if ( $e & 0xff ); + return $e >> 8; +} + +# Run a program; printing out the command line if $verbose is set +sub mysystem(@) { + print STDERR join(' ', @_), "\n" if ( $verbose ); + my $cmd = shift; + open(INPUT, "<&STDIN"); # dup STDIN filehandle to INPUT + my $childpid = open3("<&INPUT", ">&STDOUT", ">&STDERR", $cmd, @_); + waitpid ($childpid, 0); + return $?; +} + +# +# Initialization +# +open(NULL, '+<', '/dev/null') or die "$0: cannot open /dev/null\n"; + +# +# Begin parsing options. +# + +@ccopt = (); +@ldopt = (); +@libs = (); + +@files = (); # List of files +%flang = (); # Languages for files + +# This is 'c' for compile only, 'E' for preprocess only, +# 'S' for compile to assembly. +$operation = ''; # Compile and link + +# Current -x option. If undefined, it means autodetect. +undef $lang; + +$save_temps = 0; # The -save-temps option +$verbose = 0; # The -v option +$shared = 0; # Are we compiling shared? +$debugging = 0; # -g or -p option present? +$strip = 0; # -s option present? +undef $output; # -o option present? + +while ( defined($a = shift(@ARGV)) ) { + if ( $a !~ /^\-/ ) { + # Not an option. Must be a filename then. + push(@files, $a); + $flang{$a} = $lang || filename2lang($a); + } elsif ( $a eq '-' ) { + # gcc gets its input from stdin + push(@files, $a); + # prevent setting -x + $flang{$a} = 'stdin' + } elsif ( $a =~ /^-print-klibc-(.*)$/ ) { + # This test must precede -print + if ( defined($conf{$1}) ) { + print ${$conf{$1}}, "\n"; + exit 0; + } else { + die "$0: unknown option: $a\n"; + } + } elsif ( $a =~ /^(-print|-dump|--help|--version|-v)/ ) { + # These share prefixes with some other options, so put this test early! + # Pseudo-operations; just pass to gcc and don't do anything else + push(@ccopt, $a); + $operation = 'c' if ( $operation eq '' ); + } elsif ( $a =~ /^-Wl,(.*)$/ ) { + # -Wl used to pass options to the linker + push(@ldopt, split(/,/, $1)); + } elsif ( $a =~ /^-([fmwWQdO]|std=|ansi|pedantic|M[GPD]|MMD)/ ) { + # Options to gcc + push(@ccopt, $a); + } elsif ( $a =~ /^-([DUI]|M[FQT])(.*)$/ ) { + # Options to gcc, which can take either a conjoined argument + # (-DFOO) or a disjoint argument (-D FOO) + push(@ccopt, $a); + push(@ccopt, shift(@ARGV)) if ( $2 eq '' ); + } elsif ( $a eq '-include' ) { + # Options to gcc which always take a disjoint argument + push(@ccopt, $a, shift(@ARGV)); + } elsif ( $a eq '-M' || $a eq '-MM' ) { + # gcc options, that force preprocessing mode + push(@ccopt, $a); + $operation = 'E'; + } elsif ( $a =~ /^--param/ ) { + # support --param name=value and --param=name=value + my @values=split('=', $a); + if ( @values == 1 ) { + push(@ccopt, $a); + push(@ccopt, shift(@ARGV)); + } + elsif ( @values == 3 ) { + push(@ccopt, $values[0]); + push(@ccopt, join('=', $values[1],$values[2])); + } + } elsif ( $a =~ /^-[gp]/ || $a eq '-p' ) { + # Debugging options to gcc + push(@ccopt, $a); + $debugging = 1; + } elsif ( $a eq '-v' ) { + push(@ccopt, $a); + $verbose = 1; + } elsif ( $a eq '-save-temps' ) { + push(@ccopt, $a); + $save_temps = 1; + } elsif ( $a =~ '^-([cSE])$' ) { + push(@ccopt, $a); + $operation = $1; + } elsif ( $a eq '-shared' ) { + $shared = 1; + } elsif ( $a eq '-static' ) { + $shared = 0; + } elsif ( $a eq '-s' ) { + $strip = 1; + } elsif ( $a eq '-o' ) { + $output = shift(@ARGV); + } elsif ( $a =~ /^\-x(.*)$/ ) { + # -x can be conjoined or disjoined + $lang = $1; + if ( $lang eq '' ) { + $lang = shift(@ARGV); + } + } elsif ( $a eq '-nostdinc' ) { + push(@ccopt, $a); + @includes = (); + } elsif ( $a =~ /^-([lL])(.*)$/ ) { + # Libraries + push(@libs, $a); + push(@libs, shift(@ARGV)) if ( $2 eq '' ); + } else { + die "$0: unknown option: $a\n"; + } +} + +if ( $debugging ) { + @ccopt = (@REQFLAGS, @includes, @goptopt, @ccopt); +} else { + @ccopt = (@REQFLAGS, @includes, @optopt, @ccopt); +} + +if ( $operation ne '' ) { + # Just run gcc with the appropriate options + @outopt = ('-o', $output) if ( defined($output) ); + $rv = mysystem(@CC, @ccopt, @outopt, files_with_lang(\@files, \%flang)); +} else { + if ( scalar(@files) == 0 ) { + die "$0: No input files!\n"; + } + + @outopt = ('-o', $output || 'a.out'); + + @objs = (); + @rmobjs = (); + + foreach $f ( @files ) { + if ( $flang{$f} eq 'obj' ) { + push(@objs, $f); + } else { + $fo = $f; + $fo =~ s/\.[^\/.]+$/\.o/; + + die if ( $f eq $fo ); # safety check + + push(@objs, $fo); + push(@rmobjs, $fo) unless ( $save_temps ); + + $rv = mysystem(@CC, @ccopt, '-c', '-o', $fo, '-x', $flang{$f}, $f); + + if ( $rv ) { + unlink(@rmobjs); + exit syserr($rv); + } + } + } + + # Get the libgcc pathname for the *current* gcc + open(LIBGCC, '-|', @CC, @ccopt, '-print-libgcc-file-name') + or die "$0: cannot get libgcc filename\n"; + $libgcc = <LIBGCC>; + chomp $libgcc; + close(LIBGCC); + + if ( $shared ) { + $rv = mysystem(@LD, @LDFLAGS, @sharedopt, @ldopt, @outopt, @objs, + @libs, @stdlibpath, '--start-group', @sharedlib, + $libgcc, '--end-group'); + } else { + $rv = mysystem(@LD, @LDFLAGS, @staticopt, @ldopt, @outopt, @objs, + @libs, @stdlibpath, '--start-group', @staticlib, + $libgcc, '--end-group'); + } + + unlink(@rmobjs); + + if ( $strip && !$rv ) { + $rv = mysystem(@STRIP, @STRIPFLAGS, $output); + } +} + +exit syserr($rv); diff --git a/klcc/makeklcc.pl b/klcc/makeklcc.pl new file mode 100644 index 0000000..41c5cf4 --- /dev/null +++ b/klcc/makeklcc.pl @@ -0,0 +1,63 @@ +#!/usr/bin/perl +# +# Combine klibc.config, klcc.in to produce a klcc script +# +# Usage: makeklcc klcc.in klibc.config perlpath +# + +use File::Spec; + +($klccin, $klibcconf, $perlpath) = @ARGV; + +sub pathsearch($) { + my($file) = @_; + my(@path); + my($p,$pp); + + if ( $file =~ /\// ) { + return File::Spec->rel2abs($file); + } + + foreach $p ( split(/\:/, $ENV{'PATH'}) ) { + $pp = File::Spec->rel2abs(File::Spec->catpath(undef, $p, $file)); + return $pp if ( -x $pp ); + } + + return undef; +} + +print "#!${perlpath}\n"; + +open(KLIBCCONF, "< $klibcconf\0") + or die "$0: cannot open $klibcconf: $!\n"; +while ( defined($l = <KLIBCCONF>) ) { + chomp $l; + if ( $l =~ /^([^=]+)\=\s*(.*)$/ ) { + $n = $1; $s = $2; + my @s = split(/\s+/, $s); + + if ( $n eq 'CC' || $n eq 'LD' || $n eq 'STRIP' ) { + $s1 = pathsearch($s[0]); + die "$0: Cannot find $n: $s\n" unless ( defined($s1) ); + $s[0] = $s1; + } + + print "\$$n = \"\Q$s\E\";\n"; + print "\$conf{\'\L$n\E\'} = \\\$$n;\n"; + + print "\@$n = ("; $sep = ''; + for (@s) { + print $sep, "\"\Q$_\E\""; + $sep = ', '; + } + print ");\n"; + } +} +close(KLIBCCONF); + +open(KLCCIN, "< $klccin\0") +or die "$0: cannot open $klccin: $!\n"; +while ( defined($l = <KLCCIN>) ) { + print $l; +} +close(KLCCIN); diff --git a/klibc.spec b/klibc.spec new file mode 100644 index 0000000..bff0ac4 --- /dev/null +++ b/klibc.spec @@ -0,0 +1,127 @@ +Summary: A minimal libc subset for use with initramfs. +Name: klibc +Version: 2.0.13 +Release: 1 +License: BSD/GPL +Group: Development/Libraries +URL: https://www.zytor.com/mailman/listinfo/klibc +Source: https://www.kernel.org/pub/linux/libs/klibc/2.0/klibc-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +BuildRequires: kernel >= 2.6.0, kernel-devel +Packager: H. Peter Anvin <hpa@zytor.com> +Prefix: /usr +Vendor: Starving Linux Artists + +%define klibcdir %{_prefix}/lib/klibc +%define libdocdir %{_docdir}/%{name}-%{version}-%{release} +%define bindocdir %{_docdir}/%{name}-utils-%{version}-%{release} + +%description +%{name} is intended to be a minimalistic libc subset for use with +initramfs. It is deliberately written for small size, minimal +entanglement, and portability, not speed. + +%package devel +Summary: Libraries and tools needed to compile applications against klibc. +Group: Development/Libraries +Requires: klibc = %{version}-%{release} + +%description devel +This package contains the link libraries, header files, and gcc +wrapper scripts needed to compile applications against klibc. + +%package utils +Summary: Small utilities built with klibc. +Group: Utilities/System +Requires: klibc = %{version}-%{release} + +%description utils +This package contains a collection of programs that are linked against +klibc. These duplicate some of the functionality of a regular Linux +toolset, but are typically much smaller than their full-function +counterparts. They are intended for inclusion in initramfs images and +embedded systems. + +%prep +%setup -q +cp -dRs /lib/modules/`uname -r`/build/ ./linux +# Shouldn't need this when getting the build tree from /lib/modules +# make -C linux defconfig ARCH=%{_target_cpu} +# make -C linux prepare ARCH=%{_target_cpu} +# Deal with braindamage in RedHat's kernel-source RPM +rm -f linux/include/linux/config.h +cat <<EOF > linux/include/linux/config.h +#ifndef _LINUX_CONFIG_H +#define _LINUX_CONFIG_H + +#include <linux/autoconf.h> + +#endif +EOF +mkdir -p %{buildroot} + +%build +make %{_smp_mflags} \ + KLIBCARCH=%{_target_cpu} prefix=%{_prefix} bindir=%{_bindir} \ + INSTALLDIR=%{klibcdir} mandir=%{_mandir} INSTALLROOT=%{buildroot} + +%install +rm -rf %{buildroot} +make KLIBCARCH=%{_target_cpu} prefix=%{_prefix} bindir=%{_bindir} \ + INSTALLDIR=%{klibcdir} mandir=%{_mandir} INSTALLROOT=%{buildroot} \ + install + +# Make the .so file in /lib a hardlink (they will be expanded as two +# files automatically if it crosses filesystems when extracted.) +ln -f %{buildroot}%{klibcdir}/lib/klibc-*.so %{buildroot}/lib + +# Install the docs +mkdir -p %{buildroot}%{bindocdir} %{buildroot}%{libdocdir} +install -m 444 README %{buildroot}%{libdocdir} +install -m 444 usr/klibc/README.klibc %{buildroot}%{libdocdir} +install -m 444 usr/klibc/arch/README.klibc.arch %{buildroot}%{libdocdir} + +install -m 444 usr/gzip/COPYING %{buildroot}%{bindocdir}/COPYING.gzip +install -m 444 usr/gzip/README %{buildroot}%{bindocdir}/README.gzip +install -m 444 usr/kinit/ipconfig/README.ipconfig %{buildroot}%{bindocdir} +install -m 444 usr/kinit/README %{buildroot}%{bindocdir}/README.kinit + +%clean +rm -rf $RPM_BUILD_ROOT + +# +# Note: libc.so and shared-stub.o are technically -devel files, but +# put them in this package until we can make really, really sure +# the dependency system can avoid confusion. (In fact, it would be +# good to eventually get them out of here, so that multiple runtimes +# can be installed should it be necessary.) +# +%files +%defattr(-,root,root,-) +/lib/klibc-*.so +%{klibcdir}/lib/*.so +%{klibcdir}/lib/shared-stub.o + +%files devel +%defattr(-,root,root,-) +%{klibcdir}/include +%{klibcdir}/lib/*.a +%{klibcdir}/lib/crt0.o +%{_bindir}/klcc +%doc %{_mandir}/man1/* +%doc %{libdocdir}/* + +%files utils +%defattr(-,root,root,-) +%{klibcdir}/bin +%doc %{bindocdir}/* + +%changelog +* Tue Mar 1 2005 H. Peter Anvin <hpa@zytor.com> +- New "make install" scheme, klcc + +* Tue Jul 6 2004 H. Peter Anvin <hpa@zytor.com> +- Update to use kernel-source RPM for the kernel symlink. + +* Sat Nov 29 2003 Bryan O'Sullivan <bos@serpentine.com> - +- Initial build. diff --git a/klibc.spec.in b/klibc.spec.in new file mode 100644 index 0000000..db10363 --- /dev/null +++ b/klibc.spec.in @@ -0,0 +1,127 @@ +Summary: A minimal libc subset for use with initramfs. +Name: klibc +Version: @@VERSION@@ +Release: 1 +License: BSD/GPL +Group: Development/Libraries +URL: https://www.zytor.com/mailman/listinfo/klibc +Source: https://www.kernel.org/pub/linux/libs/klibc/2.0/klibc-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +BuildRequires: kernel >= 2.6.0, kernel-devel +Packager: H. Peter Anvin <hpa@zytor.com> +Prefix: /usr +Vendor: Starving Linux Artists + +%define klibcdir %{_prefix}/lib/klibc +%define libdocdir %{_docdir}/%{name}-%{version}-%{release} +%define bindocdir %{_docdir}/%{name}-utils-%{version}-%{release} + +%description +%{name} is intended to be a minimalistic libc subset for use with +initramfs. It is deliberately written for small size, minimal +entanglement, and portability, not speed. + +%package devel +Summary: Libraries and tools needed to compile applications against klibc. +Group: Development/Libraries +Requires: klibc = %{version}-%{release} + +%description devel +This package contains the link libraries, header files, and gcc +wrapper scripts needed to compile applications against klibc. + +%package utils +Summary: Small utilities built with klibc. +Group: Utilities/System +Requires: klibc = %{version}-%{release} + +%description utils +This package contains a collection of programs that are linked against +klibc. These duplicate some of the functionality of a regular Linux +toolset, but are typically much smaller than their full-function +counterparts. They are intended for inclusion in initramfs images and +embedded systems. + +%prep +%setup -q +cp -dRs /lib/modules/`uname -r`/build/ ./linux +# Shouldn't need this when getting the build tree from /lib/modules +# make -C linux defconfig ARCH=%{_target_cpu} +# make -C linux prepare ARCH=%{_target_cpu} +# Deal with braindamage in RedHat's kernel-source RPM +rm -f linux/include/linux/config.h +cat <<EOF > linux/include/linux/config.h +#ifndef _LINUX_CONFIG_H +#define _LINUX_CONFIG_H + +#include <linux/autoconf.h> + +#endif +EOF +mkdir -p %{buildroot} + +%build +make %{_smp_mflags} \ + KLIBCARCH=%{_target_cpu} prefix=%{_prefix} bindir=%{_bindir} \ + INSTALLDIR=%{klibcdir} mandir=%{_mandir} INSTALLROOT=%{buildroot} + +%install +rm -rf %{buildroot} +make KLIBCARCH=%{_target_cpu} prefix=%{_prefix} bindir=%{_bindir} \ + INSTALLDIR=%{klibcdir} mandir=%{_mandir} INSTALLROOT=%{buildroot} \ + install + +# Make the .so file in /lib a hardlink (they will be expanded as two +# files automatically if it crosses filesystems when extracted.) +ln -f %{buildroot}%{klibcdir}/lib/klibc-*.so %{buildroot}/lib + +# Install the docs +mkdir -p %{buildroot}%{bindocdir} %{buildroot}%{libdocdir} +install -m 444 README %{buildroot}%{libdocdir} +install -m 444 usr/klibc/README.klibc %{buildroot}%{libdocdir} +install -m 444 usr/klibc/arch/README.klibc.arch %{buildroot}%{libdocdir} + +install -m 444 usr/gzip/COPYING %{buildroot}%{bindocdir}/COPYING.gzip +install -m 444 usr/gzip/README %{buildroot}%{bindocdir}/README.gzip +install -m 444 usr/kinit/ipconfig/README.ipconfig %{buildroot}%{bindocdir} +install -m 444 usr/kinit/README %{buildroot}%{bindocdir}/README.kinit + +%clean +rm -rf $RPM_BUILD_ROOT + +# +# Note: libc.so and shared-stub.o are technically -devel files, but +# put them in this package until we can make really, really sure +# the dependency system can avoid confusion. (In fact, it would be +# good to eventually get them out of here, so that multiple runtimes +# can be installed should it be necessary.) +# +%files +%defattr(-,root,root,-) +/lib/klibc-*.so +%{klibcdir}/lib/*.so +%{klibcdir}/lib/shared-stub.o + +%files devel +%defattr(-,root,root,-) +%{klibcdir}/include +%{klibcdir}/lib/*.a +%{klibcdir}/lib/crt0.o +%{_bindir}/klcc +%doc %{_mandir}/man1/* +%doc %{libdocdir}/* + +%files utils +%defattr(-,root,root,-) +%{klibcdir}/bin +%doc %{bindocdir}/* + +%changelog +* Tue Mar 1 2005 H. Peter Anvin <hpa@zytor.com> +- New "make install" scheme, klcc + +* Tue Jul 6 2004 H. Peter Anvin <hpa@zytor.com> +- Update to use kernel-source RPM for the kernel symlink. + +* Sat Nov 29 2003 Bryan O'Sullivan <bos@serpentine.com> - +- Initial build. diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include new file mode 100644 index 0000000..5604b3e --- /dev/null +++ b/scripts/Kbuild.include @@ -0,0 +1,282 @@ +#### +# kbuild: Generic definitions + +# Convenient variables +comma := , +squote := ' +empty := +space := $(empty) $(empty) + +### +# Name of target with a '.' as filename prefix. foo/bar.o => foo/.bar.o +dot-target = $(dir $@).$(notdir $@) + +### +# The temporary file to save gcc -MD generated dependencies must not +# contain a comma +depfile = $(subst $(comma),_,$(dot-target).d) + +### +# filename of target with directory and extension stripped +basetarget = $(basename $(notdir $@)) + +### +# filename of first prerequisite with directory and extension stripped +baseprereq = $(basename $(notdir $<)) + +### +# Escape single quote for use in echo statements +escsq = $(subst $(squote),'\$(squote)',$1) + +### +# Easy method for doing a status message + kecho := : + quiet_kecho := echo +silent_kecho := : +kecho := $($(quiet)kecho) + +### +# filechk is used to check if the content of a generated file is updated. +# Sample usage: +# define filechk_sample +# echo $KERNELRELEASE +# endef +# version.h : Makefile +# $(call filechk,sample) +# The rule defined shall write to stdout the content of the new file. +# The existing file will be compared with the new one. +# - If no file exist it is created +# - If the content differ the new file is used +# - If they are equal no change, and no timestamp update +# - stdin is piped in from the first prerequisite ($<) so one has +# to specify a valid file as first prerequisite (often the kbuild file) +define filechk + $(Q)set -e; \ + $(kecho) ' CHK $@'; \ + mkdir -p $(dir $@); \ + $(filechk_$(1)) < $< > $@.tmp; \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + $(kecho) ' UPD $@'; \ + mv -f $@.tmp $@; \ + fi +endef + +###### +# gcc support functions +# See documentation in Documentation/kbuild/makefiles.txt + +# cc-cross-prefix +# Usage: CROSS_COMPILE := $(call cc-cross-prefix, m68k-linux-gnu- m68k-linux-) +# Return first prefix where a prefix$(CC) is found in PATH. +# If no $(CC) found in PATH with listed prefixes return nothing +cc-cross-prefix = \ + $(word 1, $(foreach c,$(1), \ + $(shell set -e; \ + if (which $(strip $(c))$(CC)) > /dev/null 2>&1 ; then \ + echo $(c); \ + fi))) + +# output directory for tests below +TMPOUT := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/) + +# try-run +# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise) +# Exit code chooses option. "$$TMP" is can be used as temporary file and +# is automatically cleaned up. +try-run = $(shell set -e; \ + TMP="$(TMPOUT).$$$$.tmp"; \ + TMPO="$(TMPOUT).$$$$.o"; \ + if ($(1)) >/dev/null 2>&1; \ + then echo "$(2)"; \ + else echo "$(3)"; \ + fi; \ + rm -f "$$TMP" "$$TMPO") + +# as-option +# Usage: cflags-y += $(call as-option,-Wa$(comma)-isa=foo,) + +as-option = $(call try-run,\ + $(CC) $(KBUILD_CFLAGS) $(1) -c -xassembler /dev/null -o "$$TMP",$(1),$(2)) + +# as-instr +# Usage: cflags-y += $(call as-instr,instr,option1,option2) + +as-instr = $(call try-run,\ + printf "%b\n" "$(1)" | $(CC) $(KBUILD_AFLAGS) -c -xassembler -o "$$TMP" -,$(2),$(3)) + +# cc-option +# Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586) + +cc-option = $(call try-run,\ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -Werror -c -xc /dev/null -o "$$TMP",$(1),$(2)) + +# cc-option-yn +# Usage: flag := $(call cc-option-yn,-march=winchip-c6) +cc-option-yn = $(call try-run,\ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -Werror -c -xc /dev/null -o "$$TMP",y,n) + +# cc-option-align +# Prefix align with either -falign or -malign +cc-option-align = $(subst -functions=0,,\ + $(call cc-option,-falign-functions=0,-malign-functions=0)) + +# cc-disable-warning +# Usage: cflags-y += $(call cc-disable-warning,unused-but-set-variable) +cc-disable-warning = $(call try-run,\ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -W$(strip $(1)) -c -xc /dev/null -o "$$TMP",-Wno-$(strip $(1))) + +# cc-name +# Expands to either gcc or clang +cc-name = $(shell $(CC) -v 2>&1 | grep -q "clang version" && echo clang || echo gcc) + +# cc-version +# Usage gcc-ver := $(call cc-version) +cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC)) + +# cc-fullversion +# Usage gcc-ver := $(call cc-fullversion) +cc-fullversion = $(shell $(CONFIG_SHELL) \ + $(srctree)/scripts/gcc-version.sh -p $(CC)) + +# cc-ifversion +# Usage: EXTRA_CFLAGS += $(call cc-ifversion, -lt, 0402, -O1) +cc-ifversion = $(shell [ $(call cc-version, $(CC)) $(1) $(2) ] && echo $(3)) + +# cc-ldoption +# Usage: ldflags += $(call cc-ldoption, -Wl$(comma)--hash-style=both) +cc-ldoption = $(call try-run,\ + $(CC) $(1) -nostdlib -xc /dev/null -o "$$TMP",$(1),$(2)) + +# ld-option +# Usage: LDFLAGS += $(call ld-option, -X) +ld-option = $(call try-run,\ + $(CC) /dev/null -c -o "$$TMPO" ; $(LD) $(1) "$$TMPO" -o "$$TMP",$(1),$(2)) + +# ar-option +# Usage: KBUILD_ARFLAGS := $(call ar-option,D) +# Important: no spaces around options +ar-option = $(call try-run, $(AR) rc$(1) "$$TMP",$(1),$(2)) + +###### + +### +# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj= +# Usage: +# $(Q)$(MAKE) $(build)=dir +build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj + +### +# Shorthand for $(Q)$(MAKE) -f scripts/Kbuild.klibc obj= +# Usage: +# $(Q)$(MAKE) $(klibc)=dir +klibc := -f $(srctree)/scripts/Kbuild.klibc obj + +# Prefix -I with $(srctree) if it is not an absolute path. +# skip if -I has no parameter +addtree = $(if $(patsubst -I%,%,$(1)), \ +$(if $(filter-out -I/%,$(1)),$(patsubst -I%,-I$(srctree)/%,$(1))) $(1)) + +# Find all -I options and call addtree +flags = $(foreach o,$($(1)),$(if $(filter -I%,$(o)),$(call addtree,$(o)),$(o))) + +# echo command. +# Short version is used, if $(quiet) equals `quiet_', otherwise full one. +echo-cmd = $(if $($(quiet)cmd_$(1)),\ + echo ' $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';) + +# printing commands +cmd = @$(echo-cmd) $(cmd_$(1)) + +# Add $(obj)/ for paths that are not absolute +objectify = $(foreach o,$(1),$(if $(filter /%,$(o)),$(o),$(obj)/$(o))) + +### +# if_changed - execute command if any prerequisite is newer than +# target, or command line has changed +# if_changed_dep - as if_changed, but uses fixdep to reveal dependencies +# including used config symbols +# if_changed_rule - as if_changed but execute rule instead +# See Documentation/kbuild/makefiles.txt for more info + +ifneq ($(KBUILD_NOCMDDEP),1) +# Check if both arguments has same arguments. Result is empty string if equal. +# User may override this check using make KBUILD_NOCMDDEP=1 +arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ + $(filter-out $(cmd_$@), $(cmd_$(1))) ) +else +arg-check = $(if $(strip $(cmd_$@)),,1) +endif + +# >'< substitution is for echo to work, +# >$< substitution to preserve $ when reloading .cmd file +# note: when using inline perl scripts [perl -e '...$$t=1;...'] +# in $(cmd_xxx) double $$ your perl vars +make-cmd = $(subst \#,\\\#,$(subst $$,$$$$,$(call escsq,$(cmd_$(1))))) + +# Find any prerequisites that is newer than target or that does not exist. +# PHONY targets skipped in both cases. +any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^) + +# Execute command if command has changed or prerequisite(s) are updated. +# +if_changed = $(if $(strip $(any-prereq) $(arg-check)), \ + @set -e; \ + $(echo-cmd) $(cmd_$(1)); \ + echo 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd) + +# Execute the command and also postprocess generated .d dependencies file. +if_changed_dep = $(if $(strip $(any-prereq) $(arg-check) ), \ + @set -e; \ + $(echo-cmd) $(cmd_$(1)); \ + scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\ + rm -f $(depfile); \ + mv -f $(dot-target).tmp $(dot-target).cmd) + +# Usage: $(call if_changed_rule,foo) +# Will check if $(cmd_foo) or any of the prerequisites changed, +# and if so will execute $(rule_foo). +if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ), \ + @set -e; \ + $(rule_$(1))) + +### +# why - tell why a a target got build +# enabled by make V=2 +# Output (listed in the order they are checked): +# (1) - due to target is PHONY +# (2) - due to target missing +# (3) - due to: file1.h file2.h +# (4) - due to command line change +# (5) - due to missing .cmd file +# (6) - due to target not in $(targets) +# (1) PHONY targets are always build +# (2) No target, so we better build it +# (3) Prerequisite is newer than target +# (4) The command line stored in the file named dir/.target.cmd +# differed from actual command line. This happens when compiler +# options changes +# (5) No dir/.target.cmd file (used to store command line) +# (6) No dir/.target.cmd file and target not listed in $(targets) +# This is a good hint that there is a bug in the kbuild file +ifeq ($(KBUILD_VERBOSE),2) +why = \ + $(if $(filter $@, $(PHONY)),- due to target is PHONY, \ + $(if $(wildcard $@), \ + $(if $(strip $(any-prereq)),- due to: $(any-prereq), \ + $(if $(arg-check), \ + $(if $(cmd_$@),- due to command line change, \ + $(if $(filter $@, $(targets)), \ + - due to missing .cmd file, \ + - due to $(notdir $@) not in $$(targets) \ + ) \ + ) \ + ) \ + ), \ + - due to target missing \ + ) \ + ) + +echo-why = $(call escsq, $(strip $(why))) +endif diff --git a/scripts/Kbuild.install b/scripts/Kbuild.install new file mode 100644 index 0000000..df02159 --- /dev/null +++ b/scripts/Kbuild.install @@ -0,0 +1,134 @@ +# +# Install klibc +# +# File is logically seperated in two pieces. +# First piece is used when during a recursive descend of the klibc tree +# and second piece is used to do the final steps in the install +# If KLIBC_INSTALL is defined it tells us we are descending and we +# use first piece of the file. + +# This indicates the location of the final version of the shared library. +# THIS MUST BE AN ABSOLUTE PATH WITH NO FINAL SLASH. +# Leave this empty to make it the root. +# +SHLIBDIR = /lib + +# First rule +PHONY := __install install-rule +__install: + +# Install commands +install-data := install -m 644 +install-lib := install -m 755 +install-bin := install -m 755 + +# Install command +quiet_cmd_install = INSTALL $(install-y) + cmd_install = $(install-bin) $(install-y) \ + $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)bin + +# Link install command +quiet_cmd_install_links = LN $(install-link-y) + cmd_install_links = $(foreach l, $(install-link-y), ln -f $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)bin/$(word 2,$(subst =,$(space),$(l))) $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)bin/$(word 1,$(subst =,$(space),$(l))) &&) true + +ifeq ($(KLIBC_INSTALL),1) +# First part - we are descending.. + +# Reset variables (to get right type of assingment) +subdir- := + +# Read .config if it exist, otherwise ignore +-include $(objtree)/.config + +# Include Kbuild file +include $(srctree)/scripts/Kbuild.include + +# Arch specific definitions for klibc +include $(srctree)/$(KLIBCSRC)/arch/$(KLIBCARCHDIR)/MCONFIG + +include $(srctree)/$(obj)/Kbuild + +# Directories to visit +# First find directories specified in lib-?, static-y and shared-y +find-dir = $(patsubst %/,%,$(filter %/, $(1))) + +__subdir := $(call find-dir, $(lib-)) +__subdir += $(call find-dir, $(lib-y)) + +__subdir += $(foreach e, $(static-y), $(call find-dir, $(e))) +__subdir += $(foreach e, $(shared-y), $(call find-dir, $(e))) + +# Use subdir- in Kbuild file to tell kbuild to visit a specific dir +subdir- += $(__subdir) + +# Remove duplicates and add prefix +subdir- := $(addprefix $(obj)/,$(sort $(subdir-))) + +# Files to install +install-y := $(strip $(addprefix $(obj)/, $(install-y))) + +__install: $(subdir-) install-rule +ifneq ($(install-y),) + $(call cmd,install) +else + @: +endif +ifneq ($(install-link-y),) + $(call cmd,install_links) +endif + +# Descending +PHONY += $(subdir-) +$(subdir-): + $(Q)$(MAKE) KLIBC_INSTALL=1 \ + -f $(srctree)/scripts/Kbuild.install obj=$@ + +# If quiet is set, only print short version of command +cmd = @$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))' &&) $(cmd_$(1)) + + +else +########################################################################## +# This is the first time this file is invoked, so kick off the +# install process. +# First we descend all sub-directories to let them do their install. +# Second we do the final install steps. + +# Do actual install as a three steps approach +# 1) Create directories, install headers and man pages +# 2) Tell that we now install binaries +# 3) Install binaries by descending +PHONY += header footer descend +header: + $(Q)echo " INSTALL headers + man pages to $(INSTALLROOT)$(INSTALLDIR)" + $(Q)mkdir -p $(INSTALLROOT)$(bindir) + $(Q)mkdir -p $(INSTALLROOT)$(mandir)/man1 + $(Q)mkdir -p $(INSTALLROOT)$(SHLIBDIR) + $(Q)mkdir -p $(INSTALLROOT)$(INSTALLDIR) + $(Q)-rm -rf $(INSTALLROOT)$(INSTALLDIR)/$(KCROSS)include + $(Q)mkdir -p $(INSTALLROOT)$(INSTALLDIR)/$(KCROSS)include + $(Q)mkdir -p $(INSTALLROOT)$(INSTALLDIR)/$(KCROSS)lib + $(Q)mkdir -p $(INSTALLROOT)$(INSTALLDIR)/$(KCROSS)bin + $(Q)cp -rfL $(KLIBCKERNELSRC)/include/. $(INSTALLROOT)$(INSTALLDIR)/$(KCROSS)include/. +ifneq ($(srctree),$(objtree)) + $(Q)cp -rf $(srctree)/usr/include/. $(INSTALLROOT)$(INSTALLDIR)/$(KCROSS)include/. +endif + $(Q)cp -rf usr/include/. $(INSTALLROOT)$(INSTALLDIR)/$(KCROSS)include/. + $(Q)chmod -R a+rX,go-w $(INSTALLROOT)$(INSTALLDIR)/$(KCROSS)include + $(Q)$(install-data) $(srctree)/klcc/klcc.1 $(INSTALLROOT)$(mandir)/man1/$(KCROSS)klcc.1 + $(Q)$(install-bin) $(objtree)/klcc/$(KCROSS)klcc $(INSTALLROOT)$(bindir) + +footer: header + $(Q)echo " INSTALL binaries to $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)bin" + +descend: footer + $(Q)$(MAKE) KLIBC_INSTALL=1 \ + -f $(srctree)/scripts/Kbuild.install obj=$(obj) + +__install: descend + @: +endif + +# Declare the contents of the PHONY variable as phony. We keep the variable for +# if_changed. +.PHONY: $(PHONY) diff --git a/scripts/Kbuild.klibc b/scripts/Kbuild.klibc new file mode 100644 index 0000000..accf7f1 --- /dev/null +++ b/scripts/Kbuild.klibc @@ -0,0 +1,440 @@ +# ========================================================================== +# Support for building klibc programs and klibc library +# ========================================================================== +# +# To create a kbuild file for a userspace program do the following: +# +# Kbuild: +# +# static-y := cat +# # This will compile a file named cat.c -> the executable 'cat' +# # The executable will be linked statically +# +# shared-y := cats +# # This will compile a file named cats.c -> the executable 'cats' +# # The executable will be linked shared +# +# If the userspace program consist of composite files do the following: +# Kbuild: +# +# static-y := kinit +# kinit-y := main.o netdev.c +# So kinit will be linked statically using the two .o files +# specified with kinit-y. +# +# Are part of the program located in a sub-directory do like this: +# kinit-y += ipconfig/ +# +# And in the subdirectory: +# ipconfig/Kbuild: +# lib-y := packet.o dhcp_proto.o +# # All .o files listed with lib-y will be used to create a single .a file. +# # The .a file is created before any subdirectories are visited so it +# # may be used in the sub-directory programs. +# +##### +# For a klibc libary file do like this +# klibc/Kbuild +# klib-y := error.o pipe.o zlib/ +# +##### +# Handling of compiler/linker options +# +# To set directory wide CFLAGS use: +# EXTRA_KLIBCCFLAGS := -DDEBUG +# To set directory wide AFLAGS use: +# EXTRA_KLIBCAFLAGS := -DDEBUG +# +# To set target specific CFLAGS (for .c files) use +# KLIBCCFLAGS-main.o := -DDEBUG=3 +# To set target specific AFLAGS (for .s files) use +# KLIBCAFLAGS-main.o := -DDEBUG=3 + +src := $(obj) +# Preset target and make sure it is a ':=' variable +targets := + +PHONY := __build +__build: + +# Read .config if it exist, otherwise ignore +-include $(objtree)/.config + +# Generic Kbuild routines +include $(srctree)/scripts/Kbuild.include + +# Defines used when compiling early userspace (klibc programs) +# --------------------------------------------------------------------------- + +KLIBCREQFLAGS := $(call cc-option, -fno-stack-protector, ) \ + $(call cc-option, -fwrapv, ) \ + $(call cc-option, -fno-PIE, ) \ + $(call cc-option, -fno-builtin-bcmp, ) \ + $(call cc-option, -fcommon, ) \ + -ggdb +KLIBCARCHREQFLAGS := +KLIBCOPTFLAGS := +KLIBCWARNFLAGS := -W -Wall -Wno-sign-compare -Wno-unused-parameter +KLIBCSHAREDFLAGS := +KLIBCBITSIZE := +KLIBCLDFLAGS := +KLIBCCFLAGS := + +# Defaults for arch to override +KLIBCARCHINCFLAGS = +KLIBCCRTSHARED := $(KLIBCOBJ)/shared-stub.o + +LD_IS_LLD := $(shell $(LD) --version 2>&1 | grep LLD) +LD_IMAGE_BASE_OPT := $(if $(LD_IS_LLD),--image-base,-Ttext-segment) + +# Arch specific definitions for klibc +include $(srctree)/$(KLIBCSRC)/arch/$(KLIBCARCHDIR)/MCONFIG + +# include/asm-* architecture +KLIBCASMARCH ?= $(KLIBCARCH) + +# klibc version +KLIBCMAJOR := $(shell cut -d. -f1 $(srctree)/usr/klibc/version) +KLIBCMINOR := $(shell cut -d. -f2 $(srctree)/usr/klibc/version) + +# binutils +KLIBCLD := $(LD) +KLIBCCC := $(CC) +KLIBCAR := $(AR) + +KLIBCRANLIB := $(RANLIB) -D +KLIBCSTRIP := $(if $(CONFIG_DEBUG_INFO),true,$(STRIP)) +KLIBCNM := $(NM) +KLIBCOBJCOPY := $(OBJCOPY) +KLIBCOBJDUMP := $(OBJDUMP) + +# klibc include paths +KLIBCCPPFLAGS := -nostdinc -iwithprefix include \ + -I$(KLIBCINC)/arch/$(KLIBCARCHDIR) \ + -I$(KLIBCINC)/bits$(KLIBCBITSIZE) \ + -I$(KLIBCOBJ)/../include \ + -I$(KLIBCINC) +ifeq ($(cc-name),clang) +KLIBCCPPFLAGS += -isystem $(shell $(KLIBCCC) $(KLIBCCFLAGS) --print-file-name=include) +endif + +# kernel include paths +KLIBCKERNELSRC ?= $(srctree) +KLIBCCPPFLAGS += -I$(KLIBCKERNELSRC)/include \ + $(if $(KBUILD_SRC),-I$(srctree)/include) \ + $(KLIBCARCHINCFLAGS) + +# compiler/assembler option for whether we want an executable stack +KLIBCSTACKFLAGS := -Wa,--$(if $(filter n,$(KLIBCEXECSTACK)),no)execstack + +# klibc definitions +KLIBCDEFS += -D__KLIBC__=$(KLIBCMAJOR) \ + -D__KLIBC_MINOR__=$(KLIBCMINOR) \ + -D_BITSIZE=$(KLIBCBITSIZE) +KLIBCCPPFLAGS += $(KLIBCDEFS) +KLIBCCFLAGS += $(KLIBCCPPFLAGS) $(KLIBCREQFLAGS) $(KLIBCARCHREQFLAGS) \ + $(KLIBCOPTFLAGS) $(KLIBCSTACKFLAGS) $(KLIBCWARNFLAGS) +KLIBCAFLAGS += -D__ASSEMBLY__ $(KLIBCCFLAGS) +KLIBCSTRIPFLAGS += --strip-all -R .comment -R .note + +KLIBCLIBGCC_DEF := $(shell $(KLIBCCC) $(KLIBCCFLAGS) $(if $(filter gcc,$(cc-name)),--print-libgcc,--print-libgcc-file-name)) +KLIBCLIBGCC ?= $(KLIBCLIBGCC_DEF) +KLIBCCRT0 := $(KLIBCOBJ)/arch/$(KLIBCARCHDIR)/crt0.o +KLIBCLIBC := $(KLIBCOBJ)/libc.a +KLIBCLIBCSHARED := $(KLIBCOBJ)/libc.so +# How to tell the linker main() is the entrypoint +KLIBCEMAIN ?= -e main + +ifdef CONFIG_DEBUG_INFO +KLIBCLDFLAGS += --build-id=sha1 +endif + +# +# This indicates the location of the final version of the shared library. +# THIS MUST BE AN ABSOLUTE PATH WITH NO FINAL SLASH. +# Leave this empty to make it the root. +# +SHLIBDIR = /lib + +export KLIBCLD KLIBCCC KLIBCAR KLIBCSTRIP KLIBCNM +export KLIBCCFLAGS KLIBCAFLAGS KLIBCLIBGCC KLIBCSHAREDFLAGS KLIBCSTRIPFLAGS +export KLIBCCRT0 KLIBCLIBC SHLIBDIR + +# Add $(obj)/ for paths that is not absolute +objectify = $(foreach o,$(1),$(if $(filter /%,$(o)),$(o),$(obj)/$(o))) + +# Kbuild file in the directory that is being build +include $(srctree)/$(obj)/Kbuild + +##### +# static-y + shared-y handling +kprogs := $(static-y) $(shared-y) +# kprogs based on a single .o file (with same name + .o) +kprog-objs := $(foreach p, $(kprogs), $(if $($(p)-y),,$(p))) +kprog-objs := $(addsuffix .o, $(kprog-objs)) +# kprogs which is based on several .o files +kprog-multi := $(foreach p, $(kprogs), $(if $($(p)-y),$(p))) +# objects used for kprogs with more then one .o file +kprog-objs += $(foreach p, $(kprog-multi), $($(p)-y)) +# objects build in this dir +kprog-real-objs := $(patsubst %/,,$(kprog-objs)) +# Directories we need to visit before kprogs-objs are up-to-date +kprog-dirs := $(patsubst %/,%,$(filter %/, $(kprog-objs))) +# replace all dir/ with dir/lib.a +kprog-objs := $(patsubst %/, %/lib.a, $(kprog-objs)) + +targets += $(static-y) $(shared-y) + +##### +# klib-y handling +# .o files to build in this dir +klib-real-objs := $(patsubst %/,,$(klib-y)) +# Directories we need to visit before libs are up-to-date +klib-dirs := $(patsubst %/,%,$(filter %/, $(klib-y))) +# replace all dir/ with dir/klib.list +klib-objs := $(patsubst %/, %/klib.list, $(klib-y)) + +# $(output-dirs) are a list of directories that contain object files +output-dirs := $(dir $(kprog-dirs) $(kprog-objs)) +output-dirs += $(foreach f, $(hostprogs-y) $(targets), \ + $(if $(dir $(f)), $(dir $(f)))) +output-dirs += $(dir $(klib-objs)) +output-dirs := $(strip $(sort $(filter-out ./,$(output-dirs)))) + +# prefix so we get full dir +static-y := $(addprefix $(obj)/,$(static-y)) +shared-y := $(addprefix $(obj)/,$(shared-y)) +kprog-objs := $(addprefix $(obj)/,$(kprog-objs)) +kprog-real-objs := $(addprefix $(obj)/,$(kprog-real-objs)) +output-dirs := $(addprefix $(obj)/,$(output-dirs)) +kprog-dirs := $(addprefix $(obj)/,$(kprog-dirs)) +subdir-y := $(addprefix $(obj)/,$(subdir-y)) +always := $(addprefix $(obj)/,$(always)) +targets := $(addprefix $(obj)/,$(targets)) +lib-y := $(addprefix $(obj)/,$(lib-y)) +klib-y := $(addprefix $(obj)/,$(klib-y)) +klib-objs := $(addprefix $(obj)/,$(klib-objs)) +klib-real-objs := $(addprefix $(obj)/,$(klib-real-objs)) +klib-dirs := $(addprefix $(obj)/,$(klib-dirs)) + +##### +# Handle options to gcc. Support building with separate output directory + +__klibccflags = $(KLIBCCFLAGS) $(EXTRA_KLIBCCFLAGS) $(KLIBCCFLAGS_$(*F).o) +__klibcaflags = $(KLIBCAFLAGS) $(EXTRA_KLIBCAFLAGS) $(KLIBCAFLAGS_$(*F).o) + +_klibccflags = $(call flags,__klibccflags) +_klibcaflags = $(call flags,__klibcaflags) + +klibccflags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(_klibccflags) +klibcaflags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(_klibcaflags) + +# Create output directory if not already present +_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj)) + +# Create directories for object files if directory does not exist +# Needed when lib-y := dir/file.o syntax is used +_dummy := $(foreach d,$(output-dirs), $(shell [ -d $(d) ] || mkdir -p $(d))) + +# Do we have to make a lib.a in this dir? +ifneq ($(strip $(lib-y) $(lib-n) $(lib-)),) +lib-target := $(obj)/lib.a +endif + +__build: $(subdir-y) $(lib-target) $(always) + $(Q): + +# Compile C sources (.c) +# --------------------------------------------------------------------------- + +quiet_cmd_cc_s_c = KLIBCCC $@ + cmd_cc_s_c = $(KLIBCCC) $(klibccflags) -S -o $@ $< + +%.s: %.c FORCE + $(call if_changed_dep,cc_s_c) + +quiet_cmd_cc_o_c = KLIBCCC $@ + cmd_cc_o_c = $(KLIBCCC) $(klibccflags) -c -o $@ $< + +%.o: %.c FORCE + $(call if_changed_dep,cc_o_c) + +quiet_cmd_cc_i_c = CPP $@ + cmd_cc_i_c = $(KLIBCCC) -E $(klibccflags) -o $@ $< +%.i: %.c FORCE + $(call if_changed_dep,cc_i_c) + +# Compile assembler sources (.S) +# --------------------------------------------------------------------------- + +quiet_cmd_as_o_S = KLIBCAS $@ + cmd_as_o_S = $(KLIBCCC) $(klibcaflags) -c -o $@ $< + +%.o: %.S FORCE + $(call if_changed_dep,as_o_S) + +targets += $(real-objs-y) + +# +# Rule to compile a set of .o files into one .o file +# +ifdef lib-target +quiet_cmd_link_o_target = LD $@ +# If the list of objects to link is empty, just create an empty lib.a +cmd_link_o_target = $(if $(strip $(lib-y)),\ + rm -f $@; $(KLIBCAR) Dcr $@ $(filter $(lib-y), $^),\ + rm -f $@; $(KLIBCAR) Dcrs $@) + +$(lib-target): $(lib-y) FORCE + $(call if_changed,link_o_target) +targets += $(lib-target) $(lib-y) +endif # lib-target + +# +# Create klib.list +# +# Do we have to create a klibc library file in this dir? +ifneq ($(strip $(klib-y) $(klib-n) $(klib-)),) +klib-target := $(obj)/klib.list +endif + +ifdef klib-target +# include this in build +__build: $(klib-target) $(klib-dirs) + +# descend if needed +$(sort $(addsuffix /klib.list,$(klib-dirs))): $(klib-dirs) ; + +# create klib.list +quiet_cmd_klib-list = LIST $@ + cmd_klib-list = echo $(klib-real-objs) > $@ +$(klib-target): $(klib-objs) FORCE + $(call if_changed,klib-list) +targets += $(klib-target) $(klib-real-objs) +endif # klib-target + +ifdef kprogs +# Compile klibc-programs for the target +# =========================================================================== + +__build : $(kprog-dirs) $(static-y) +ifdef KLIBCSHAREDFLAGS +__build : $(shared-y) +endif + +# Descend if needed +$(sort $(addsuffix /lib.a,$(kprog-dirs))): $(kprog-dirs) ; + +# Define dependencies for link of progs +# For the simple program: +# file.o => file +# A program with multiple objects +# filea.o, fileb.o => file +# A program with .o files in another dir +# dir/lib.a filea.o => file + +stripobj = $(subst $(obj)/,,$@) +addliba = $(addprefix $(obj)/, $(patsubst %/, %/lib.a, $(1))) +link-deps = $(if $($(stripobj)-y), $(call addliba, $($(stripobj)-y)), $@.o) \ + $(call objectify,$($(stripobj)-lib)) + +quiet_cmd_ld-static = KLIBCLD $@ + cmd_ld-static = $(KLIBCLD) $(KLIBCLDFLAGS) -o $@ \ + $(EXTRA_KLIBCLDFLAGS) \ + $(KLIBCCRT0) \ + --start-group \ + $(link-deps) \ + $(KLIBCLIBC) \ + $(KLIBCLIBGCC) \ + --end-group ; \ + cp -f $@ $@.g ; \ + $(KLIBCSTRIP) $(KLIBCSTRIPFLAGS) $@ + + +$(static-y): $(kprog-objs) $(lib-target) $(KLIBCCRT0) $(KLIBCLIBC) FORCE + $(call if_changed,ld-static) + +quiet_cmd_ld-shared = KLIBCLD $@ + cmd_ld-shared = $(KLIBCLD) $(KLIBCLDFLAGS) -o $@ \ + $(EXTRA_KLIBCLDFLAGS) \ + $(KLIBCEMAIN) $(KLIBCCRTSHARED) \ + --start-group \ + $(link-deps) \ + --just-symbols $(KLIBCLIBCSHARED) \ + $(KLIBCLIBGCC) \ + --end-group ; \ + cp -f $@ $@.g ; \ + $(KLIBCSTRIP) $(KLIBCSTRIPFLAGS) $@ + + +$(shared-y): $(kprog-objs) $(lib-target) $(KLIBCCRTSHARED) \ + $(KLIBCLIBCSHARED) FORCE + $(call if_changed,ld-shared) + +# Do not try to build KLIBC libaries if we are building klibc +ifeq ($(klibc-build),) +$(KLIBCCRT0) $(KLIBCLIBC): ; +$(KLIBCCRTSHARED) $(KLIBCLIBCSHARED): ; +endif + +targets += $(kprog-real-objs) +endif + +# Compile programs on the host +# =========================================================================== +ifdef hostprogs-y +include $(srctree)/scripts/Makefile.host +endif + +# Descending +# --------------------------------------------------------------------------- + +PHONY += $(subdir-y) $(kprog-dirs) $(klib-dirs) +$(sort $(subdir-y) $(kprog-dirs) $(klib-dirs)): $(lib-target) + $(Q)$(MAKE) $(klibc)=$@ + +# Add FORCE to the prequisites of a target to force it to be always rebuilt. +# --------------------------------------------------------------------------- + +PHONY += FORCE + +FORCE: + +# Linking +# Create a reloctable composite object file +# --------------------------------------------------------------------------- +quiet_cmd_klibcld = KLIBCLD $@ + cmd_klibcld = $(KLIBCLD) -r $(KLIBCLDFLAGS) \ + $(EXTRA_KLIBCLDFLAGS) $(KLIBCLDFLAGS_$(@F)) \ + $(filter-out FORCE,$^) -o $@ + + +# Link target to a new name +# --------------------------------------------------------------------------- +quiet_cmd_ln = LN $@ + cmd_ln = rm -f $@ && ln $< $@ + +# Strip target (remove all debugging info) +quiet_cmd_strip = STRIP $@ + cmd_strip = $(KLIBCSTRIP) $(KLIBCSTRIPFLAGS) $< -o $@ + + +# Read all saved command lines and dependencies for the $(targets) we +# may be building above, using $(if_changed{,_dep}). As an +# optimization, we don't need to read them if the target does not +# exist, we will rebuild anyway in that case. +targets := $(wildcard $(sort $(targets))) +cmd_files := $(wildcard $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) + +ifneq ($(cmd_files),) + include $(cmd_files) +endif + +# Shorthand for $(Q)$(MAKE) -f scripts/Kbuild.klibc obj +# Usage: +# $(Q)$(MAKE) $(klibc)=dir +klibc := -rR -f $(srctree)/scripts/Kbuild.klibc obj + +# Declare the contents of the PHONY variable as phony. We keep the variable for +# if_changed. +.PHONY: $(PHONY) diff --git a/scripts/Kbuild.klibc.include b/scripts/Kbuild.klibc.include new file mode 100644 index 0000000..b317286 --- /dev/null +++ b/scripts/Kbuild.klibc.include @@ -0,0 +1,11 @@ + +# klibc-cc-option +# Usage: cflags-y += $(call klibc-cc-option,-march=winchip-c6,-march=i586) + +klibc-cc-option = $(call try-run,\ + $(CC) $(KLIBCCPPFLAGS) $(KLIBCCFLAGS) $(1) -c -xc /dev/null -o "$$TMP",$(1),$(2)) + +# klibc-cc-option-yn +# Usage: flag := $(call klibc-cc-option-yn,-march=winchip-c6) +klibc-cc-option-yn = $(call try-run,\ + $(CC) $(KLIBCCPPFLAGS) $(KLIBCCFLAGS) $(1) -c -xc /dev/null -o "$$TMP",y,n) diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean new file mode 100644 index 0000000..abc4e3f --- /dev/null +++ b/scripts/Makefile.clean @@ -0,0 +1,96 @@ +# ========================================================================== +# Cleaning up +# ========================================================================== + +src := $(obj) + +.PHONY := __clean +__clean: + +# Shorthand for $(Q)$(MAKE) scripts/Makefile.clean obj=dir +# Usage: +# $(Q)$(MAKE) $(clean)=dir +clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj + +# The filename Kbuild has precedence over Makefile +kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) +include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile) + +# Figure out what we need to build from the various variables +# ========================================================================== + +subdirs := $(subdir-y) $(subdir-m) $(subdir-n) $(subdir-) +subdirs += $(patsubst %/,%,$(filter %/, $(obj-y))) +subdirs += $(patsubst %/,%,$(filter %/, $(obj-m))) +subdirs += $(patsubst %/,%,$(filter %/, $(obj-n))) +subdirs += $(patsubst %/,%,$(filter %/, $(obj-))) + +subdirs += $(patsubst %/,%,$(filter %/, $(klib-y))) +subdirs += $(patsubst %/,%,$(filter %/, $(klib-))) + +# Subdirectories we need to descend into +subdirs := $(addprefix $(obj)/,$(sort $(subdirs))) + + +# build a list of files to remove, usually releative to the current +# directory + +__clean-files := $(extra-y) $(EXTRA_TARGETS) $(always) \ + $(targets) $(clean-files) \ + $(host-progs) \ + $(hostprogs-y) $(hostprogs-m) $(hostprogs-) \ + klib.list + +# as clean-files is given relative to the current directory, this adds +# a $(obj) prefix, except for absolute paths + +__clean-files := $(wildcard \ + $(addprefix $(obj)/, $(filter-out /%, $(__clean-files))) \ + $(filter /%, $(__clean-files))) + +# as clean-dirs is given relative to the current directory, this adds +# a $(obj) prefix, except for absolute paths + +__clean-dirs := $(wildcard \ + $(addprefix $(obj)/, $(filter-out /%, $(clean-dirs))) \ + $(filter /%, $(clean-dirs))) + +# ========================================================================== + +quiet_cmd_clean = CLEAN $(obj) + cmd_clean = rm -f $(__clean-files) +quiet_cmd_cleandir = CLEAN $(__clean-dirs) + cmd_cleandir = rm -rf $(__clean-dirs) + + +__clean: $(subdirs) +ifneq ($(strip $(__clean-files)),) + +$(call cmd,clean) +endif +ifneq ($(strip $(__clean-dirs)),) + +$(call cmd,cleandir) +endif +ifneq ($(strip $(clean-rule)),) + +$(clean-rule) +endif + @: + + +# =========================================================================== +# Generic stuff +# =========================================================================== + +# Descending +# --------------------------------------------------------------------------- + +PHONY += $(subdirs) +$(subdirs): + $(Q)$(MAKE) $(clean)=$@ + +# If quiet is set, only print short version of command + +cmd = @$(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))' &&) $(cmd_$(1)) + +# Declare the contents of the PHONY variable as phony. We keep the variable for +# if_changed. +.PHONY: $(PHONY) diff --git a/scripts/Makefile.host b/scripts/Makefile.host new file mode 100644 index 0000000..6a6b949 --- /dev/null +++ b/scripts/Makefile.host @@ -0,0 +1,160 @@ +# ========================================================================== +# Building binaries on the host system +# Binaries are used during the compilation of the kernel, for example +# to preprocess a data file. +# +# Both C and C++ is supported, but preferred language is C for such utilities. +# +# Sample syntax (see Documentation/kbuild/makefile.txt for reference) +# hostprogs-y := bin2hex +# Will compile bin2hex.c and create an executable named bin2hex +# +# hostprogs-y := lxdialog +# lxdialog-objs := checklist.o lxdialog.o +# Will compile lxdialog.c and checklist.c, and then link the executable +# lxdialog, based on checklist.o and lxdialog.o +# +# hostprogs-y := qconf +# qconf-cxxobjs := qconf.o +# qconf-objs := menu.o +# Will compile qconf as a C++ program, and menu as a C program. +# They are linked as C++ code to the executable qconf + +# hostprogs-y := conf +# conf-objs := conf.o libkconfig.so +# libkconfig-objs := expr.o type.o +# Will create a shared library named libkconfig.so that consist of +# expr.o and type.o (they are both compiled as C code and the object file +# are made as position independent code). +# conf.c is compiled as a c program, and conf.o is linked together with +# libkconfig.so as the executable conf. +# Note: Shared libraries consisting of C++ files are not supported + +__hostprogs := $(sort $(hostprogs-y)$(hostprogs-m)) + +# hostprogs-y := tools/build may have been specified. Retreive directory +obj-dirs += $(foreach f,$(__hostprogs), $(if $(dir $(f)),$(dir $(f)))) +obj-dirs := $(strip $(sort $(filter-out ./,$(obj-dirs)))) + + +# C code +# Executables compiled from a single .c file +host-csingle := $(foreach m,$(__hostprogs),$(if $($(m)-objs),,$(m))) + +# C executables linked based on several .o files +host-cmulti := $(foreach m,$(__hostprogs),\ + $(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m)))) + +# Object (.o) files compiled from .c files +host-cobjs := $(sort $(foreach m,$(__hostprogs),$($(m)-objs))) + +# C++ code +# C++ executables compiled from at least on .cc file +# and zero or more .c files +host-cxxmulti := $(foreach m,$(__hostprogs),$(if $($(m)-cxxobjs),$(m))) + +# C++ Object (.o) files compiled from .cc files +host-cxxobjs := $(sort $(foreach m,$(host-cxxmulti),$($(m)-cxxobjs))) + +# Shared libaries (only .c supported) +# Shared libraries (.so) - all .so files referenced in "xxx-objs" +host-cshlib := $(sort $(filter %.so, $(host-cobjs))) +# Remove .so files from "xxx-objs" +host-cobjs := $(filter-out %.so,$(host-cobjs)) + +#Object (.o) files used by the shared libaries +host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-objs)))) + +__hostprogs := $(addprefix $(obj)/,$(__hostprogs)) +host-csingle := $(addprefix $(obj)/,$(host-csingle)) +host-cmulti := $(addprefix $(obj)/,$(host-cmulti)) +host-cobjs := $(addprefix $(obj)/,$(host-cobjs)) +host-cxxmulti := $(addprefix $(obj)/,$(host-cxxmulti)) +host-cxxobjs := $(addprefix $(obj)/,$(host-cxxobjs)) +host-cshlib := $(addprefix $(obj)/,$(host-cshlib)) +host-cshobjs := $(addprefix $(obj)/,$(host-cshobjs)) +obj-dirs := $(addprefix $(obj)/,$(obj-dirs)) + +##### +# Handle options to gcc. Support building with separate output directory + +# Prefix -I with $(srctree) if it is not an absolute path +addtree = $(if $(filter-out -I/%,$(1)),$(patsubst -I%,-I$(srctree)/%,$(1))) $(1) +# Find all -I options and call addtree +flags = $(foreach o,$($(1)),$(if $(filter -I%,$(o)),$(call addtree,$(o)),$(o))) + +_hostc_flags = $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) $(HOSTCFLAGS_$(*F).o) +_hostcxx_flags = $(HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) $(HOSTCXXFLAGS_$(*F).o) + +ifeq ($(KBUILD_SRC),) +__hostc_flags = $(_hostc_flags) +__hostcxx_flags = $(_hostcxx_flags) +else +__hostc_flags = -I$(obj) $(call flags,_hostc_flags) +__hostcxx_flags = -I$(obj) $(call flags,_hostcxx_flags) +endif + +hostc_flags = -Wp,-MD,$(depfile) $(__hostc_flags) +hostcxx_flags = -Wp,-MD,$(depfile) $(__hostcxx_flags) + +##### +# Compile programs on the host + +# Create executable from a single .c file +# host-csingle -> Executable +quiet_cmd_host-csingle = HOSTCC $@ + cmd_host-csingle = $(HOSTCC) $(hostc_flags) -o $@ $< \ + $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) +$(host-csingle): %: %.c FORCE + $(call if_changed_dep,host-csingle) + +# Link an executable based on list of .o files, all plain c +# host-cmulti -> executable +quiet_cmd_host-cmulti = HOSTLD $@ + cmd_host-cmulti = $(HOSTCC) $(HOSTLDFLAGS) -o $@ \ + $(addprefix $(obj)/,$($(@F)-objs)) \ + $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) +$(host-cmulti): %: $(host-cobjs) $(host-cshlib) FORCE + $(call if_changed,host-cmulti) + +# Create .o file from a single .c file +# host-cobjs -> .o +quiet_cmd_host-cobjs = HOSTCC $@ + cmd_host-cobjs = $(HOSTCC) $(hostc_flags) -c -o $@ $< +$(host-cobjs): %.o: %.c FORCE + $(call if_changed_dep,host-cobjs) + +# Link an executable based on list of .o files, a mixture of .c and .cc +# host-cxxmulti -> executable +quiet_cmd_host-cxxmulti = HOSTLD $@ + cmd_host-cxxmulti = $(HOSTCXX) $(HOSTLDFLAGS) -o $@ \ + $(foreach o,objs cxxobjs,\ + $(addprefix $(obj)/,$($(@F)-$(o)))) \ + $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) +$(host-cxxmulti): %: $(host-cobjs) $(host-cxxobjs) $(host-cshlib) FORCE + $(call if_changed,host-cxxmulti) + +# Create .o file from a single .cc (C++) file +quiet_cmd_host-cxxobjs = HOSTCXX $@ + cmd_host-cxxobjs = $(HOSTCXX) $(hostcxx_flags) -c -o $@ $< +$(host-cxxobjs): %.o: %.cc FORCE + $(call if_changed_dep,host-cxxobjs) + +# Compile .c file, create position independent .o file +# host-cshobjs -> .o +quiet_cmd_host-cshobjs = HOSTCC -fPIC $@ + cmd_host-cshobjs = $(HOSTCC) $(hostc_flags) -fPIC -c -o $@ $< +$(host-cshobjs): %.o: %.c FORCE + $(call if_changed_dep,host-cshobjs) + +# Link a shared library, based on position independent .o files +# *.o -> .so shared library (host-cshlib) +quiet_cmd_host-cshlib = HOSTLLD -shared $@ + cmd_host-cshlib = $(HOSTCC) $(HOSTLDFLAGS) -shared -o $@ \ + $(addprefix $(obj)/,$($(@F:.so=-objs))) \ + $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) +$(host-cshlib): %: $(host-cshobjs) FORCE + $(call if_changed,host-cshlib) + +targets += $(host-csingle) $(host-cmulti) $(host-cobjs)\ + $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs) diff --git a/scripts/basic/.gitignore b/scripts/basic/.gitignore new file mode 100644 index 0000000..a776371 --- /dev/null +++ b/scripts/basic/.gitignore @@ -0,0 +1 @@ +fixdep diff --git a/scripts/basic/Kbuild b/scripts/basic/Kbuild new file mode 100644 index 0000000..22e09d2 --- /dev/null +++ b/scripts/basic/Kbuild @@ -0,0 +1,6 @@ +# +# Kbuild file to build basic kbuild programs +# + +hostprogs-y := fixdep +always := $(hostprogs-y) diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c new file mode 100644 index 0000000..fed4c7f --- /dev/null +++ b/scripts/basic/fixdep.c @@ -0,0 +1,390 @@ +/* + * "Optimize" a list of dependencies as spit out by gcc -MD + * for the kernel build + * =========================================================================== + * + * Author Kai Germaschewski + * Copyright 2002 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * + * Introduction: + * + * gcc produces a very nice and correct list of dependencies which + * tells make when to remake a file. + * + * To use this list as-is however has the drawback that virtually + * every file in the kernel includes <linux/config.h> which then again + * includes <linux/autoconf.h> + * + * If the user re-runs make *config, linux/autoconf.h will be + * regenerated. make notices that and will rebuild every file which + * includes autoconf.h, i.e. basically all files. This is extremely + * annoying if the user just changed CONFIG_HIS_DRIVER from n to m. + * + * So we play the same trick that "mkdep" played before. We replace + * the dependency on linux/autoconf.h by a dependency on every config + * option which is mentioned in any of the listed prequisites. + * + * To be exact, split-include populates a tree in include/config/, + * e.g. include/config/his/driver.h, which contains the #define/#undef + * for the CONFIG_HIS_DRIVER option. + * + * So if the user changes his CONFIG_HIS_DRIVER option, only the objects + * which depend on "include/linux/config/his/driver.h" will be rebuilt, + * so most likely only his driver ;-) + * + * The idea above dates, by the way, back to Michael E Chastain, AFAIK. + * + * So to get dependencies right, there are two issues: + * o if any of the files the compiler read changed, we need to rebuild + * o if the command line given to the compile the file changed, we + * better rebuild as well. + * + * The former is handled by using the -MD output, the later by saving + * the command line used to compile the old object and comparing it + * to the one we would now use. + * + * Again, also this idea is pretty old and has been discussed on + * kbuild-devel a long time ago. I don't have a sensibly working + * internet connection right now, so I rather don't mention names + * without double checking. + * + * This code here has been based partially based on mkdep.c, which + * says the following about its history: + * + * Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>. + * This is a C version of syncdep.pl by Werner Almesberger. + * + * + * It is invoked as + * + * fixdep <depfile> <target> <cmdline> + * + * and will read the dependency file <depfile> + * + * The transformed dependency snipped is written to stdout. + * + * It first generates a line + * + * cmd_<target> = <cmdline> + * + * and then basically copies the .<target>.d file to stdout, in the + * process filtering out the dependency on linux/autoconf.h and adding + * dependencies on include/config/my/option.h for every + * CONFIG_MY_OPTION encountered in any of the prequisites. + * + * It will also filter out all the dependencies on *.ver. We need + * to make sure that the generated version checksum are globally up + * to date before even starting the recursive build, so it's too late + * at this point anyway. + * + * The algorithm to grep for "CONFIG_..." is bit unusual, but should + * be fast ;-) We don't even try to really parse the header files, but + * merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will + * be picked up as well. It's not a problem with respect to + * correctness, since that can only give too many dependencies, thus + * we cannot miss a rebuild. Since people tend to not mention totally + * unrelated CONFIG_ options all over the place, it's not an + * efficiency problem either. + * + * (Note: it'd be easy to port over the complete mkdep state machine, + * but I don't think the added complexity is worth it) + */ +/* + * Note 2: if somebody writes HELLO_CONFIG_BOOM in a file, it will depend onto + * CONFIG_BOOM. This could seem a bug (not too hard to fix), but please do not + * fix it! Some UserModeLinux files (look at arch/um/) call CONFIG_BOOM as + * UML_CONFIG_BOOM, to avoid conflicts with /usr/include/linux/autoconf.h, + * through arch/um/include/uml-config.h; this fixdep "bug" makes sure that + * those files will have correct dependencies. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <ctype.h> +#include <arpa/inet.h> + +#define INT_CONF ntohl(0x434f4e46) +#define INT_ONFI ntohl(0x4f4e4649) +#define INT_NFIG ntohl(0x4e464947) +#define INT_FIG_ ntohl(0x4649475f) + +char *target; +char *depfile; +char *cmdline; + +static void usage(void) +{ + fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n"); + exit(1); +} + +static void print_cmdline(void) +{ + printf("cmd_%s := %s\n\n", target, cmdline); +} + +char * str_config = NULL; +int size_config = 0; +int len_config = 0; + +/* + * Grow the configuration string to a desired length. + * Usually the first growth is plenty. + */ +static void grow_config(int len) +{ + while (len_config + len > size_config) { + if (size_config == 0) + size_config = 2048; + str_config = realloc(str_config, size_config *= 2); + if (str_config == NULL) + { perror("fixdep:malloc"); exit(1); } + } +} + + + +/* + * Lookup a value in the configuration string. + */ +static int is_defined_config(const char *name, int len) +{ + const char * pconfig; + const char * plast = str_config + len_config - len; + for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) { + if (pconfig[ -1] == '\n' + && pconfig[len] == '\n' + && !memcmp(pconfig, name, len)) + return 1; + } + return 0; +} + +/* + * Add a new value to the configuration string. + */ +static void define_config(const char *name, int len) +{ + grow_config(len + 1); + + memcpy(str_config+len_config, name, len); + len_config += len; + str_config[len_config++] = '\n'; +} + +/* + * Clear the set of configuration strings. + */ +static void clear_config(void) +{ + len_config = 0; + define_config("", 0); +} + +/* + * Record the use of a CONFIG_* word. + */ +static void use_config(char *m, int slen) +{ + char s[PATH_MAX]; + char *p; + + if (is_defined_config(m, slen)) + return; + + define_config(m, slen); + + memcpy(s, m, slen); s[slen] = 0; + + for (p = s; p < s + slen; p++) { + if (*p == '_') + *p = '/'; + else + *p = tolower((int)*p); + } + printf(" $(wildcard include/config/%s.h) \\\n", s); +} + +static void parse_config_file(char *map, size_t len) +{ + int *end = (int *) (map + len); + /* start at +1, so that p can never be < map */ + int *m = (int *) map + 1; + char *p, *q; + + for (; m < end; m++) { + if (*m == INT_CONF) { p = (char *) m ; goto conf; } + if (*m == INT_ONFI) { p = (char *) m-1; goto conf; } + if (*m == INT_NFIG) { p = (char *) m-2; goto conf; } + if (*m == INT_FIG_) { p = (char *) m-3; goto conf; } + continue; + conf: + if (p > map + len - 7) + continue; + if (memcmp(p, "CONFIG_", 7)) + continue; + for (q = p + 7; q < map + len; q++) { + if (!(isalnum(*q) || *q == '_')) + goto found; + } + continue; + + found: + use_config(p+7, q-p-7); + } +} + +/* test is s ends in sub */ +static int strrcmp(char *s, char *sub) +{ + int slen = strlen(s); + int sublen = strlen(sub); + + if (sublen > slen) + return 1; + + return memcmp(s + slen - sublen, sub, sublen); +} + +static void do_config_file(char *filename) +{ + struct stat st; + int fd; + void *map; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "fixdep: "); + perror(filename); + exit(2); + } + fstat(fd, &st); + if (st.st_size == 0) { + close(fd); + return; + } + map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if ((long) map == -1) { + perror("fixdep: mmap"); + close(fd); + return; + } + + parse_config_file(map, st.st_size); + + munmap(map, st.st_size); + + close(fd); +} + +static void parse_dep_file(void *map, size_t len) +{ + char *m = map; + char *end = m + len; + char *p; + char s[PATH_MAX]; + + p = strchr(m, ':'); + if (!p) { + fprintf(stderr, "fixdep: parse error\n"); + exit(1); + } + memcpy(s, m, p-m); s[p-m] = 0; + printf("deps_%s := \\\n", target); + m = p+1; + + clear_config(); + + while (m < end) { + while (m < end && (*m == ' ' || *m == '\\' || *m == '\n')) + m++; + p = m; + while (p < end && *p != ' ') p++; + if (p == end) { + do p--; while (!isalnum(*p)); + p++; + } + memcpy(s, m, p-m); s[p-m] = 0; + if (strrcmp(s, "include/linux/autoconf.h") && + strrcmp(s, "arch/um/include/uml-config.h") && + strrcmp(s, ".ver")) { + printf(" %s \\\n", s); + do_config_file(s); + } + m = p + 1; + } + printf("\n%s: $(deps_%s)\n\n", target, target); + printf("$(deps_%s):\n", target); +} + +static void print_deps(void) +{ + struct stat st; + int fd; + void *map; + + fd = open(depfile, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "fixdep: "); + perror(depfile); + exit(2); + } + fstat(fd, &st); + if (st.st_size == 0) { + fprintf(stderr,"fixdep: %s is empty\n",depfile); + close(fd); + return; + } + map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if ((long) map == -1) { + perror("fixdep: mmap"); + close(fd); + return; + } + + parse_dep_file(map, st.st_size); + + munmap(map, st.st_size); + + close(fd); +} + +static void traps(void) +{ + static char test[] __attribute__((aligned(sizeof(int)))) = "CONF"; + int *p = (int *)test; + + if (*p != INT_CONF) { + fprintf(stderr, "fixdep: sizeof(int) != 4 or wrong endianess? %#x\n", + *p); + exit(2); + } +} + +int main(int argc, char *argv[]) +{ + traps(); + + if (argc != 4) + usage(); + + depfile = argv[1]; + target = argv[2]; + cmdline = argv[3]; + + print_cmdline(); + print_deps(); + + return 0; +} diff --git a/usr/.gitignore b/usr/.gitignore new file mode 100644 index 0000000..77534ee --- /dev/null +++ b/usr/.gitignore @@ -0,0 +1,2 @@ +# Generated headers (we don't want a .gitignore in the include directory!) +/include/klibc/havesyscall.h diff --git a/usr/Kbuild b/usr/Kbuild new file mode 100644 index 0000000..890811c --- /dev/null +++ b/usr/Kbuild @@ -0,0 +1,75 @@ +# +# kbuild file for usr/ - including initramfs image and klibc +# + +CONFIG_KLIBC := 1 + +include-subdir := include +klibc-subdir := klibc +usr-subdirs := kinit utils dash gzip +subdir- := $(include-subdir) $(klibc-subdir) $(usr-subdirs) + +usr-subdirs := $(addprefix _usr_,$(usr-subdirs)) +klibc-subdir := $(addprefix _usr_,$(klibc-subdir)) + +# klibc binaries +ifdef CONFIG_KLIBC + +# .initramfs_data.cpio.gz.d is used to identify all files included +# in initramfs and to detect if any files are added/removed. +# Removed files are identified by directory timestamp being updated +# The dependency list is generated by gen_initramfs.sh -l +ifneq ($(wildcard $(obj)/.initramfs_data.cpio.gz.d),) + include $(obj)/.initramfs_data.cpio.gz.d +endif + +# build klibc library before the klibc programs +# build klibc programs before cpio.gz +.PHONY: initramfs $(usr-subdirs) $(klibc-subdir) $(include-subdir) +initramfs: $(usr-subdirs) $(klibc-subdir) $(include-subdir) +$(deps_initramfs): $(usr-subdirs) $(klibc-subdir) $(include-subdir) + +$(usr-subdirs): $(klibc-subdir) + $(Q)$(MAKE) $(klibc)=$(src)/$(patsubst _usr_%,%,$(@)) + +$(klibc-subdir): $(include-subdir) + $(Q)$(MAKE) $(klibc)=$(src)/$(patsubst _usr_%,%,$(@)) + +$(include-subdir): + $(Q)$(MAKE) $(klibc)=$(src)/$(patsubst _usr_%,%,$(@)) +endif + + +# Generate builtin.o based on initramfs_data.o +obj-y := initramfs_data.o + +# initramfs_data.o contains the initramfs_data.cpio.gz image. +# The image is included using .incbin, a dependency which is not +# tracked automatically. +$(obj)/initramfs_data.o: $(obj)/initramfs_data.cpio.gz FORCE + +##### +# Generate the initramfs cpio archive + +hostprogs-y := gen_init_cpio +ginitramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh +ramfs-def := $(srctree)/$(src)/initramfs.default +ramfs-input := $(shell echo $(CONFIG_INITRAMFS_SOURCE)) +ramfs-input := $(if $(ramfs-input), $(ramfs-input), $(ramfs-def)) + +ramfs-args := \ + $(if $(CONFIG_INITRAMFS_ROOT_UID), -u $(CONFIG_INITRAMFS_ROOT_UID)) \ + $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) + +quiet_cmd_initfs = GEN $@ + cmd_initfs = $(ginitramfs) -o $@ $(ramfs-args) $(ramfs-input) + +targets := initramfs_data.cpio.gz +# We rebuild initramfs_data.cpio.gz if: +# 1) Any included file is newer then initramfs_data.cpio.gz +# 2) There are changes in which files are included (added or deleted) +# 3) If gen_init_cpio are newer than initramfs_data.cpio.gz +# 4) arguments to gen_initramfs.sh changes +$(obj)/initramfs_data.cpio.gz: $(obj)/gen_init_cpio $(deps_initramfs) initramfs + $(Q)$(ginitramfs) -l $(ramfs-input) > $(obj)/.initramfs_data.cpio.gz.d + $(call if_changed,initfs) diff --git a/usr/dash/.gitignore b/usr/dash/.gitignore new file mode 100644 index 0000000..480952f --- /dev/null +++ b/usr/dash/.gitignore @@ -0,0 +1,13 @@ +arith.[ch] +.builtins.def.d +builtins.[ch] +builtins.def +init.c +mkinit +mknodes +mksyntax +nodes.[ch] +sh +sh.shared +syntax.[ch] +token.h diff --git a/usr/dash/Kbuild b/usr/dash/Kbuild new file mode 100644 index 0000000..c0f8dcb --- /dev/null +++ b/usr/dash/Kbuild @@ -0,0 +1,103 @@ +# +# Kbuild file for dash +# + +config-cppflags := -DBSD=1 -DSMALL -DJOBS=0 -DHAVE_CONFIG_H -DSHELL +config-cppflags += -DGLOB_BROKEN -DIFS_BROKEN + +EXTRA_KLIBCCFLAGS := -I$(srctree)/$(src) -I$(objtree)/$(obj) +EXTRA_KLIBCCFLAGS += -include $(srctree)/$(src)/config.h +EXTRA_KLIBCCFLAGS += $(config-cppflags) + +HOST_EXTRACFLAGS := $(config-cppflags) + +init-o-files := alias.o arith_yacc.o arith_yylex.o cd.o error.o eval.o exec.o expand.o \ + histedit.o input.o jobs.o mail.o main.o memalloc.o miscbltin.o \ + mystring.o options.o parser.o redir.o show.o trap.o output.o \ + bltin/printf.o system.o bltin/test.o var.o + +gen-o-files := builtins.o init.o nodes.o syntax.o + +static/sh-y := $(init-o-files) $(gen-o-files) + +hostprogs-y := mkinit mksyntax mknodes mksignames +gen-h-files := builtins.h nodes.h syntax.h token.h + +static-y := static/sh + +# The shared binary +shared-y := shared/sh +shared/sh-y := $(static/sh-y) + +# For cleaning +targets := static/sh static/sh.g shared/sh shared/sh.g $(gen-o-files) + +# explicit dependency for all generated files +$(addprefix $(obj)/, $(static/sh-y)): $(addprefix $(obj)/, $(gen-h-files)) + +# Generate token.h +targets += token.h +quiet_cmd_mktokens = GEN $@ + cmd_mktokens = sh $< > $@ +$(obj)/token.h: $(src)/mktokens + $(call if_changed,mktokens) + +# Generate builtins.def +targets += builtins.def +quiet_cmd_mkbuiltins_def = GEN $@ + cmd_mkbuiltins_def = $(HOSTCC) $(hostc_flags) -x c -E -o $@ $< +$(obj)/builtins.def: $(src)/builtins.def.in $(src)/config.h + $(call if_changed,mkbuiltins_def) + +# Generate builtins{.c + .h} +targets += builtins.c builtins.h +quiet_cmd_mkbuiltins = GEN $@ + cmd_mkbuiltins = mkdir -p $(obj)/bltin && cd $(obj) && \ + sh $(srctree)/$(src)/mkbuiltins builtins.def +$(obj)/builtins.c: $(src)/mkbuiltins $(obj)/builtins.def + $(call cmd,mkbuiltins) + +# side effect.. +$(obj)/builtins.h: $(obj)/builtins.c + $(Q): + +# Generate init.c +targets += init.c +init-c-files := $(addprefix $(srctree)/$(src)/, $(init-o-files:.o=.c)) +quiet_cmd_mkinit = GEN $@ + cmd_mkinit = cd $(obj) && ./mkinit $(init-c-files) +$(obj)/init.c: $(obj)/mkinit $(init-c-files) + $(call cmd,mkinit) + +# Generate nodes{.c + .h} +targets += nodes.c nodes.h +quiet_cmd_mknodes = GEN $@ + cmd_mknodes = cd $(obj) && ./mknodes $(srctree)/$(src)/nodetypes \ + $(srctree)/$(src)/nodes.c.pat +$(obj)/nodes.c: $(obj)/mknodes $(src)/nodetypes $(src)/nodes.c.pat + $(call cmd,mknodes) + +# side effect.. +$(obj)/nodes.h: $(obj)/nodes.c + $(Q): + +# Generate syntax{.c + .h} +targets += syntax.c syntax.h +quiet_cmd_mksyntax = GEN $@ + cmd_mksyntax = cd $(obj) && ./mksyntax +$(obj)/syntax.c: $(obj)/mksyntax + $(call cmd,mksyntax) + +# side effect.. +$(obj)/syntax.h: $(obj)/syntax.c + $(Q): + +# Clean deletes the static and shared dir +clean-dirs := static shared + +# Targets to install +ifdef KLIBCSHAREDFLAGS +install-y := shared/sh +else +install-y := static/sh +endif diff --git a/usr/dash/README.dash b/usr/dash/README.dash new file mode 100644 index 0000000..d67a5af --- /dev/null +++ b/usr/dash/README.dash @@ -0,0 +1,50 @@ +This version of dash was obtained from + +git://git.kernel.org/pub/scm/utils/dash/dash.git + +It corresponds up to changeset 46abc8c6d8a5e9a5712bdc1312c0b6960eec65a4 +omitting ee5cbe9fd6bc02f31b4d955606288de36c3d4eab. + +Several changes have been made for klibc: + +* The build system is changed from autotools to kbuild, with a static config.h +* Use of strtod() is guarded by HAVE_STRTOD, which we don't define +* The built-in times command, which requires floating-point support, is + disabled +* The signal_names[] array is replaced by a signal_name() function using + klibc's own signal name array. decode_signal() is changed similarly. +* The read built-in implements the -t option like bash +* mktokens is modified to support out-of-tree builds +* mkbuiltins is modified to sort with LC_ALL=C +* Some header files have header guards added +* Changelog and some manual pages are omitted +* Automatic whitespace fixups + +HOWTO sync branch: + +1) Generate mailbox of patches and fix up their paths: + + git format-patch --subject-prefix='' -N --stdout <changeset>.. \ + | awk '(FNR == 1 || blank) && /^From / { header = 1; hash = $2 } + header && /^Subject:/ { sub("^Subject:", "Subject: [klibc] dash:") } + { print; blank = /^$/ } + header && blank { + print "[ dash commit", hash, "]"; print + header = 0 + }' \ + | filterdiff --strip 2 --addoldprefix a/usr/dash/ --addnewprefix b/usr/dash/ \ + --exclude="*/configure.ac" --exclude="*/ChangeLog" \ + --exclude="*/dash.1" --exclude="*/Makefile.am" \ + --exclude="*/mksignames.c" \ + > dash.mbox + +2) Import patches from mailbox: + + git am --whitespace=fix -k -i -s ../dash/dash.mbox + +3) update config.h + +Generate klibc config.h in dash repository: + automake --add-missing + autoreconf + ./configure CC=klcc diff --git a/usr/dash/TOUR b/usr/dash/TOUR new file mode 100644 index 0000000..056e79b --- /dev/null +++ b/usr/dash/TOUR @@ -0,0 +1,343 @@ +# @(#)TOUR 8.1 (Berkeley) 5/31/93 + +NOTE -- This is the original TOUR paper distributed with ash and +does not represent the current state of the shell. It is provided anyway +since it provides helpful information for how the shell is structured, +but be warned that things have changed -- the current shell is +still under development. + +================================================================ + + A Tour through Ash + + Copyright 1989 by Kenneth Almquist. + + +DIRECTORIES: The subdirectory bltin contains commands which can +be compiled stand-alone. The rest of the source is in the main +ash directory. + +SOURCE CODE GENERATORS: Files whose names begin with "mk" are +programs that generate source code. A complete list of these +programs is: + + program intput files generates + ------- ------------ --------- + mkbuiltins builtins builtins.h builtins.c + mkinit *.c init.c + mknodes nodetypes nodes.h nodes.c + mksignames - signames.h signames.c + mksyntax - syntax.h syntax.c + mktokens - token.h + bltin/mkexpr unary_op binary_op operators.h operators.c + +There are undoubtedly too many of these. Mkinit searches all the +C source files for entries looking like: + + INIT { + x = 1; /* executed during initialization */ + } + + RESET { + x = 2; /* executed when the shell does a longjmp + back to the main command loop */ + } + +It pulls this code out into routines which are when particular +events occur. The intent is to improve modularity by isolating +the information about which modules need to be explicitly +initialized/reset within the modules themselves. + +Mkinit recognizes several constructs for placing declarations in +the init.c file. + INCLUDE "file.h" +includes a file. The storage class MKINIT makes a declaration +available in the init.c file, for example: + MKINIT int funcnest; /* depth of function calls */ +MKINIT alone on a line introduces a structure or union declara- +tion: + MKINIT + struct redirtab { + short renamed[10]; + }; +Preprocessor #define statements are copied to init.c without any +special action to request this. + +INDENTATION: The ash source is indented in multiples of six +spaces. The only study that I have heard of on the subject con- +cluded that the optimal amount to indent is in the range of four +to six spaces. I use six spaces since it is not too big a jump +from the widely used eight spaces. If you really hate six space +indentation, use the adjind (source included) program to change +it to something else. + +EXCEPTIONS: Code for dealing with exceptions appears in +exceptions.c. The C language doesn't include exception handling, +so I implement it using setjmp and longjmp. The global variable +exception contains the type of exception. EXERROR is raised by +calling error. EXINT is an interrupt. + +INTERRUPTS: In an interactive shell, an interrupt will cause an +EXINT exception to return to the main command loop. (Exception: +EXINT is not raised if the user traps interrupts using the trap +command.) The INTOFF and INTON macros (defined in exception.h) +provide uninterruptable critical sections. Between the execution +of INTOFF and the execution of INTON, interrupt signals will be +held for later delivery. INTOFF and INTON can be nested. + +MEMALLOC.C: Memalloc.c defines versions of malloc and realloc +which call error when there is no memory left. It also defines a +stack oriented memory allocation scheme. Allocating off a stack +is probably more efficient than allocation using malloc, but the +big advantage is that when an exception occurs all we have to do +to free up the memory in use at the time of the exception is to +restore the stack pointer. The stack is implemented using a +linked list of blocks. + +STPUTC: If the stack were contiguous, it would be easy to store +strings on the stack without knowing in advance how long the +string was going to be: + p = stackptr; + *p++ = c; /* repeated as many times as needed */ + stackptr = p; +The folloing three macros (defined in memalloc.h) perform these +operations, but grow the stack if you run off the end: + STARTSTACKSTR(p); + STPUTC(c, p); /* repeated as many times as needed */ + grabstackstr(p); + +We now start a top-down look at the code: + +MAIN.C: The main routine performs some initialization, executes +the user's profile if necessary, and calls cmdloop. Cmdloop is +repeatedly parses and executes commands. + +OPTIONS.C: This file contains the option processing code. It is +called from main to parse the shell arguments when the shell is +invoked, and it also contains the set builtin. The -i and -j op- +tions (the latter turns on job control) require changes in signal +handling. The routines setjobctl (in jobs.c) and setinteractive +(in trap.c) are called to handle changes to these options. + +PARSING: The parser code is all in parser.c. A recursive des- +cent parser is used. Syntax tables (generated by mksyntax) are +used to classify characters during lexical analysis. There are +three tables: one for normal use, one for use when inside single +quotes, and one for use when inside double quotes. The tables +are machine dependent because they are indexed by character vari- +ables and the range of a char varies from machine to machine. + +PARSE OUTPUT: The output of the parser consists of a tree of +nodes. The various types of nodes are defined in the file node- +types. + +Nodes of type NARG are used to represent both words and the con- +tents of here documents. An early version of ash kept the con- +tents of here documents in temporary files, but keeping here do- +cuments in memory typically results in significantly better per- +formance. It would have been nice to make it an option to use +temporary files for here documents, for the benefit of small +machines, but the code to keep track of when to delete the tem- +porary files was complex and I never fixed all the bugs in it. +(AT&T has been maintaining the Bourne shell for more than ten +years, and to the best of my knowledge they still haven't gotten +it to handle temporary files correctly in obscure cases.) + +The text field of a NARG structure points to the text of the +word. The text consists of ordinary characters and a number of +special codes defined in parser.h. The special codes are: + + CTLVAR Variable substitution + CTLENDVAR End of variable substitution + CTLBACKQ Command substitution + CTLESC Escape next character + +A variable substitution contains the following elements: + + CTLVAR type name '=' [ alternative-text CTLENDVAR ] + +The type field is a single character specifying the type of sub- +stitution. The possible types are: + + VSNORMAL $var + VSMINUS ${var-text} + VSMINUS|VSNUL ${var:-text} + VSPLUS ${var+text} + VSPLUS|VSNUL ${var:+text} + VSQUESTION ${var?text} + VSQUESTION|VSNUL ${var:?text} + VSASSIGN ${var=text} + VSASSIGN|VSNUL ${var=text} + +The name of the variable comes next, terminated by an equals +sign. If the type is not VSNORMAL, then the text field in the +substitution follows, terminated by a CTLENDVAR byte. + +Commands in back quotes are parsed and stored in a linked list. +The locations of these commands in the string are indicated by +the CTLBACKQ character. + +The character CTLESC escapes the next character, so that in case +any of the CTL characters mentioned above appear in the input, +they can be passed through transparently. CTLESC is also used to +escape '*', '?', '[', and '!' characters which were quoted by the +user and thus should not be used for file name generation. + +CTLESC characters have proved to be particularly tricky to get +right. In the case of here documents which are not subject to +variable and command substitution, the parser doesn't insert any +CTLESC characters to begin with (so the contents of the text +field can be written without any processing). Other here docu- +ments, and words which are not subject to splitting and file name +generation, have the CTLESC characters removed during the vari- +able and command substitution phase. Words which are subject +splitting and file name generation have the CTLESC characters re- +moved as part of the file name phase. + +EXECUTION: Command execution is handled by the following files: + eval.c The top level routines. + redir.c Code to handle redirection of input and output. + jobs.c Code to handle forking, waiting, and job control. + exec.c Code to to path searches and the actual exec sys call. + expand.c Code to evaluate arguments. + var.c Maintains the variable symbol table. Called from expand.c. + +EVAL.C: Evaltree recursively executes a parse tree. The exit +status is returned in the global variable exitstatus. The alter- +native entry evalbackcmd is called to evaluate commands in back +quotes. It saves the result in memory if the command is a buil- +tin; otherwise it forks off a child to execute the command and +connects the standard output of the child to a pipe. + +JOBS.C: To create a process, you call makejob to return a job +structure, and then call forkshell (passing the job structure as +an argument) to create the process. Waitforjob waits for a job +to complete. These routines take care of process groups if job +control is defined. + +REDIR.C: Ash allows file descriptors to be redirected and then +restored without forking off a child process. This is accom- +plished by duplicating the original file descriptors. The redir- +tab structure records where the file descriptors have be dupli- +cated to. + +EXEC.C: The routine find_command locates a command, and enters +the command in the hash table if it is not already there. The +third argument specifies whether it is to print an error message +if the command is not found. (When a pipeline is set up, +find_command is called for all the commands in the pipeline be- +fore any forking is done, so to get the commands into the hash +table of the parent process. But to make command hashing as +transparent as possible, we silently ignore errors at that point +and only print error messages if the command cannot be found +later.) + +The routine shellexec is the interface to the exec system call. + +EXPAND.C: Arguments are processed in three passes. The first +(performed by the routine argstr) performs variable and command +substitution. The second (ifsbreakup) performs word splitting +and the third (expandmeta) performs file name generation. If the +"/u" directory is simulated, then when "/u/username" is replaced +by the user's home directory, the flag "didudir" is set. This +tells the cd command that it should print out the directory name, +just as it would if the "/u" directory were implemented using +symbolic links. + +VAR.C: Variables are stored in a hash table. Probably we should +switch to extensible hashing. The variable name is stored in the +same string as the value (using the format "name=value") so that +no string copying is needed to create the environment of a com- +mand. Variables which the shell references internally are preal- +located so that the shell can reference the values of these vari- +ables without doing a lookup. + +When a program is run, the code in eval.c sticks any environment +variables which precede the command (as in "PATH=xxx command") in +the variable table as the simplest way to strip duplicates, and +then calls "environment" to get the value of the environment. +There are two consequences of this. First, if an assignment to +PATH precedes the command, the value of PATH before the assign- +ment must be remembered and passed to shellexec. Second, if the +program turns out to be a shell procedure, the strings from the +environment variables which preceded the command must be pulled +out of the table and replaced with strings obtained from malloc, +since the former will automatically be freed when the stack (see +the entry on memalloc.c) is emptied. + +BUILTIN COMMANDS: The procedures for handling these are scat- +tered throughout the code, depending on which location appears +most appropriate. They can be recognized because their names al- +ways end in "cmd". The mapping from names to procedures is +specified in the file builtins, which is processed by the mkbuil- +tins command. + +A builtin command is invoked with argc and argv set up like a +normal program. A builtin command is allowed to overwrite its +arguments. Builtin routines can call nextopt to do option pars- +ing. This is kind of like getopt, but you don't pass argc and +argv to it. Builtin routines can also call error. This routine +normally terminates the shell (or returns to the main command +loop if the shell is interactive), but when called from a builtin +command it causes the builtin command to terminate with an exit +status of 2. + +The directory bltins contains commands which can be compiled in- +dependently but can also be built into the shell for efficiency +reasons. The makefile in this directory compiles these programs +in the normal fashion (so that they can be run regardless of +whether the invoker is ash), but also creates a library named +bltinlib.a which can be linked with ash. The header file bltin.h +takes care of most of the differences between the ash and the +stand-alone environment. The user should call the main routine +"main", and #define main to be the name of the routine to use +when the program is linked into ash. This #define should appear +before bltin.h is included; bltin.h will #undef main if the pro- +gram is to be compiled stand-alone. + +CD.C: This file defines the cd and pwd builtins. The pwd com- +mand runs /bin/pwd the first time it is invoked (unless the user +has already done a cd to an absolute pathname), but then +remembers the current directory and updates it when the cd com- +mand is run, so subsequent pwd commands run very fast. The main +complication in the cd command is in the docd command, which +resolves symbolic links into actual names and informs the user +where the user ended up if he crossed a symbolic link. + +SIGNALS: Trap.c implements the trap command. The routine set- +signal figures out what action should be taken when a signal is +received and invokes the signal system call to set the signal ac- +tion appropriately. When a signal that a user has set a trap for +is caught, the routine "onsig" sets a flag. The routine dotrap +is called at appropriate points to actually handle the signal. +When an interrupt is caught and no trap has been set for that +signal, the routine "onint" in error.c is called. + +OUTPUT: Ash uses it's own output routines. There are three out- +put structures allocated. "Output" represents the standard out- +put, "errout" the standard error, and "memout" contains output +which is to be stored in memory. This last is used when a buil- +tin command appears in backquotes, to allow its output to be col- +lected without doing any I/O through the UNIX operating system. +The variables out1 and out2 normally point to output and errout, +respectively, but they are set to point to memout when appropri- +ate inside backquotes. + +INPUT: The basic input routine is pgetc, which reads from the +current input file. There is a stack of input files; the current +input file is the top file on this stack. The code allows the +input to come from a string rather than a file. (This is for the +-c option and the "." and eval builtin commands.) The global +variable plinno is saved and restored when files are pushed and +popped from the stack. The parser routines store the number of +the current line in this variable. + +DEBUGGING: If DEBUG is defined in shell.h, then the shell will +write debugging information to the file $HOME/trace. Most of +this is done using the TRACE macro, which takes a set of printf +arguments inside two sets of parenthesis. Example: +"TRACE(("n=%d0, n))". The double parenthesis are necessary be- +cause the preprocessor can't handle functions with a variable +number of arguments. Defining DEBUG also causes the shell to +generate a core dump if it is sent a quit signal. The tracing +code is in show.c. diff --git a/usr/dash/alias.c b/usr/dash/alias.c new file mode 100644 index 0000000..daeacbb --- /dev/null +++ b/usr/dash/alias.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <stdlib.h> +#include "shell.h" +#include "input.h" +#include "output.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "options.h" /* XXX for argptr (should remove?) */ + +#define ATABSIZE 39 + +struct alias *atab[ATABSIZE]; + +STATIC void setalias(const char *, const char *); +STATIC struct alias *freealias(struct alias *); +STATIC struct alias **__lookupalias(const char *); + +STATIC +void +setalias(const char *name, const char *val) +{ + struct alias *ap, **app; + + app = __lookupalias(name); + ap = *app; + INTOFF; + if (ap) { + if (!(ap->flag & ALIASINUSE)) { + ckfree(ap->val); + } + ap->val = savestr(val); + ap->flag &= ~ALIASDEAD; + } else { + /* not found */ + ap = ckmalloc(sizeof (struct alias)); + ap->name = savestr(name); + ap->val = savestr(val); + ap->flag = 0; + ap->next = 0; + *app = ap; + } + INTON; +} + +int +unalias(const char *name) +{ + struct alias **app; + + app = __lookupalias(name); + + if (*app) { + INTOFF; + *app = freealias(*app); + INTON; + return (0); + } + + return (1); +} + +void +rmaliases(void) +{ + struct alias *ap, **app; + int i; + + INTOFF; + for (i = 0; i < ATABSIZE; i++) { + app = &atab[i]; + for (ap = *app; ap; ap = *app) { + *app = freealias(*app); + if (ap == *app) { + app = &ap->next; + } + } + } + INTON; +} + +struct alias * +lookupalias(const char *name, int check) +{ + struct alias *ap = *__lookupalias(name); + + if (check && ap && (ap->flag & ALIASINUSE)) + return (NULL); + return (ap); +} + +/* + * TODO - sort output + */ +int +aliascmd(int argc, char **argv) +{ + char *n, *v; + int ret = 0; + struct alias *ap; + + if (argc == 1) { + int i; + + for (i = 0; i < ATABSIZE; i++) + for (ap = atab[i]; ap; ap = ap->next) { + printalias(ap); + } + return (0); + } + while ((n = *++argv) != NULL) { + if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ + if ((ap = *__lookupalias(n)) == NULL) { + outfmt(out2, "%s: %s not found\n", "alias", n); + ret = 1; + } else + printalias(ap); + } else { + *v++ = '\0'; + setalias(n, v); + } + } + + return (ret); +} + +int +unaliascmd(int argc, char **argv) +{ + int i; + + while ((i = nextopt("a")) != '\0') { + if (i == 'a') { + rmaliases(); + return (0); + } + } + for (i = 0; *argptr; argptr++) { + if (unalias(*argptr)) { + outfmt(out2, "%s: %s not found\n", "unalias", *argptr); + i = 1; + } + } + + return (i); +} + +STATIC struct alias * +freealias(struct alias *ap) { + struct alias *next; + + if (ap->flag & ALIASINUSE) { + ap->flag |= ALIASDEAD; + return ap; + } + + next = ap->next; + ckfree(ap->name); + ckfree(ap->val); + ckfree(ap); + return next; +} + +void +printalias(const struct alias *ap) { + out1fmt("%s=%s\n", ap->name, single_quote(ap->val)); +} + +STATIC struct alias ** +__lookupalias(const char *name) { + unsigned int hashval; + struct alias **app; + const char *p; + unsigned int ch; + + p = name; + + ch = (unsigned char)*p; + hashval = ch << 4; + while (ch) { + hashval += ch; + ch = (unsigned char)*++p; + } + app = &atab[hashval % ATABSIZE]; + + for (; *app; app = &(*app)->next) { + if (equal(name, (*app)->name)) { + break; + } + } + + return app; +} diff --git a/usr/dash/alias.h b/usr/dash/alias.h new file mode 100644 index 0000000..fb841d6 --- /dev/null +++ b/usr/dash/alias.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)alias.h 8.2 (Berkeley) 5/4/95 + */ + +#define ALIASINUSE 1 +#define ALIASDEAD 2 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +struct alias *lookupalias(const char *, int); +int aliascmd(int, char **); +int unaliascmd(int, char **); +void rmaliases(void); +int unalias(const char *); +void printalias(const struct alias *); diff --git a/usr/dash/arith_yacc.c b/usr/dash/arith_yacc.c new file mode 100644 index 0000000..1a087c3 --- /dev/null +++ b/usr/dash/arith_yacc.c @@ -0,0 +1,304 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <inttypes.h> +#include <stdlib.h> +#include "arith_yacc.h" +#include "expand.h" +#include "shell.h" +#include "error.h" +#include "output.h" +#include "var.h" + +#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ +#error Arithmetic tokens are out of order. +#endif + +static const char *arith_startbuf; + +const char *arith_buf; +union yystype yylval; + +static int last_token; + +#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec + +static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = { + ARITH_PRECEDENCE(ARITH_MUL, 0), + ARITH_PRECEDENCE(ARITH_DIV, 0), + ARITH_PRECEDENCE(ARITH_REM, 0), + ARITH_PRECEDENCE(ARITH_ADD, 1), + ARITH_PRECEDENCE(ARITH_SUB, 1), + ARITH_PRECEDENCE(ARITH_LSHIFT, 2), + ARITH_PRECEDENCE(ARITH_RSHIFT, 2), + ARITH_PRECEDENCE(ARITH_LT, 3), + ARITH_PRECEDENCE(ARITH_LE, 3), + ARITH_PRECEDENCE(ARITH_GT, 3), + ARITH_PRECEDENCE(ARITH_GE, 3), + ARITH_PRECEDENCE(ARITH_EQ, 4), + ARITH_PRECEDENCE(ARITH_NE, 4), + ARITH_PRECEDENCE(ARITH_BAND, 5), + ARITH_PRECEDENCE(ARITH_BXOR, 6), + ARITH_PRECEDENCE(ARITH_BOR, 7), +}; + +#define ARITH_MAX_PREC 8 + +static void yyerror(const char *s) __attribute__ ((noreturn)); +static void yyerror(const char *s) +{ + sh_error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); + /* NOTREACHED */ +} + +static inline int arith_prec(int op) +{ + return prec[op - ARITH_BINOP_MIN]; +} + +static inline int higher_prec(int op1, int op2) +{ + return arith_prec(op1) < arith_prec(op2); +} + +static intmax_t do_binop(int op, intmax_t a, intmax_t b) +{ + switch (op) { + default: + case ARITH_REM: + case ARITH_DIV: + if (!b) + yyerror("division by zero"); + return op == ARITH_REM ? a % b : a / b; + case ARITH_MUL: + return a * b; + case ARITH_ADD: + return a + b; + case ARITH_SUB: + return a - b; + case ARITH_LSHIFT: + return a << b; + case ARITH_RSHIFT: + return a >> b; + case ARITH_LT: + return a < b; + case ARITH_LE: + return a <= b; + case ARITH_GT: + return a > b; + case ARITH_GE: + return a >= b; + case ARITH_EQ: + return a == b; + case ARITH_NE: + return a != b; + case ARITH_BAND: + return a & b; + case ARITH_BXOR: + return a ^ b; + case ARITH_BOR: + return a | b; + } +} + +static intmax_t assignment(int var, int noeval); + +static intmax_t primary(int token, union yystype *val, int op, int noeval) +{ + intmax_t result; + +again: + switch (token) { + case ARITH_LPAREN: + result = assignment(op, noeval); + if (last_token != ARITH_RPAREN) + yyerror("expecting ')'"); + last_token = yylex(); + return result; + case ARITH_NUM: + last_token = op; + return val->val; + case ARITH_VAR: + last_token = op; + return noeval ? val->val : lookupvarint(val->name); + case ARITH_ADD: + token = op; + *val = yylval; + op = yylex(); + goto again; + case ARITH_SUB: + *val = yylval; + return -primary(op, val, yylex(), noeval); + case ARITH_NOT: + *val = yylval; + return !primary(op, val, yylex(), noeval); + case ARITH_BNOT: + *val = yylval; + return ~primary(op, val, yylex(), noeval); + default: + yyerror("expecting primary"); + } +} + +static intmax_t binop2(intmax_t a, int op, int prec, int noeval) +{ + for (;;) { + union yystype val; + intmax_t b; + int op2; + int token; + + token = yylex(); + val = yylval; + + b = primary(token, &val, yylex(), noeval); + + op2 = last_token; + if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX && + higher_prec(op2, op)) { + b = binop2(b, op2, arith_prec(op), noeval); + op2 = last_token; + } + + a = noeval ? b : do_binop(op, a, b); + + if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX || + arith_prec(op2) >= prec) + return a; + + op = op2; + } +} + +static intmax_t binop(int token, union yystype *val, int op, int noeval) +{ + intmax_t a = primary(token, val, op, noeval); + + op = last_token; + if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX) + return a; + + return binop2(a, op, ARITH_MAX_PREC, noeval); +} + +static intmax_t and(int token, union yystype *val, int op, int noeval) +{ + intmax_t a = binop(token, val, op, noeval); + intmax_t b; + + op = last_token; + if (op != ARITH_AND) + return a; + + token = yylex(); + *val = yylval; + + b = and(token, val, yylex(), noeval | !a); + + return a && b; +} + +static intmax_t or(int token, union yystype *val, int op, int noeval) +{ + intmax_t a = and(token, val, op, noeval); + intmax_t b; + + op = last_token; + if (op != ARITH_OR) + return a; + + token = yylex(); + *val = yylval; + + b = or(token, val, yylex(), noeval | !!a); + + return a || b; +} + +static intmax_t cond(int token, union yystype *val, int op, int noeval) +{ + intmax_t a = or(token, val, op, noeval); + intmax_t b; + intmax_t c; + + if (last_token != ARITH_QMARK) + return a; + + b = assignment(yylex(), noeval | !a); + + if (last_token != ARITH_COLON) + yyerror("expecting ':'"); + + token = yylex(); + *val = yylval; + + c = cond(token, val, yylex(), noeval | !!a); + + return a ? b : c; +} + +static intmax_t assignment(int var, int noeval) +{ + union yystype val = yylval; + int op = yylex(); + intmax_t result; + + if (var != ARITH_VAR) + return cond(var, &val, op, noeval); + + if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX)) + return cond(var, &val, op, noeval); + + result = assignment(yylex(), noeval); + if (noeval) + return result; + + return setvarint(val.name, + op == ARITH_ASS ? result : + do_binop(op - 11, lookupvarint(val.name), result), 0); +} + +intmax_t arith(const char *s) +{ + intmax_t result; + + arith_buf = arith_startbuf = s; + + result = assignment(yylex(), 0); + + if (last_token) + yyerror("expecting EOF"); + + return result; +} diff --git a/usr/dash/arith_yacc.h b/usr/dash/arith_yacc.h new file mode 100644 index 0000000..ff34d52 --- /dev/null +++ b/usr/dash/arith_yacc.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#define ARITH_ASS 1 + +#define ARITH_OR 2 +#define ARITH_AND 3 +#define ARITH_BAD 4 +#define ARITH_NUM 5 +#define ARITH_VAR 6 +#define ARITH_NOT 7 + +#define ARITH_BINOP_MIN 8 +#define ARITH_LE 8 +#define ARITH_GE 9 +#define ARITH_LT 10 +#define ARITH_GT 11 +#define ARITH_EQ 12 +#define ARITH_REM 13 +#define ARITH_BAND 14 +#define ARITH_LSHIFT 15 +#define ARITH_RSHIFT 16 +#define ARITH_MUL 17 +#define ARITH_ADD 18 +#define ARITH_BOR 19 +#define ARITH_SUB 20 +#define ARITH_BXOR 21 +#define ARITH_DIV 22 +#define ARITH_NE 23 +#define ARITH_BINOP_MAX 24 + +#define ARITH_ASS_MIN 24 +#define ARITH_REMASS 24 +#define ARITH_BANDASS 25 +#define ARITH_LSHIFTASS 26 +#define ARITH_RSHIFTASS 27 +#define ARITH_MULASS 28 +#define ARITH_ADDASS 29 +#define ARITH_BORASS 30 +#define ARITH_SUBASS 31 +#define ARITH_BXORASS 32 +#define ARITH_DIVASS 33 +#define ARITH_ASS_MAX 34 + +#define ARITH_LPAREN 34 +#define ARITH_RPAREN 35 +#define ARITH_BNOT 36 +#define ARITH_QMARK 37 +#define ARITH_COLON 38 + +union yystype { + intmax_t val; + char *name; +}; + +extern union yystype yylval; + +int yylex(void); diff --git a/usr/dash/arith_yylex.c b/usr/dash/arith_yylex.c new file mode 100644 index 0000000..ec5b5b2 --- /dev/null +++ b/usr/dash/arith_yylex.c @@ -0,0 +1,240 @@ +/*- + * Copyright (c) 2002 + * Herbert Xu. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include "arith_yacc.h" +#include "expand.h" +#include "error.h" +#include "shell.h" +#include "memalloc.h" +#include "syntax.h" +#include "system.h" + +#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ +#error Arithmetic tokens are out of order. +#endif + +extern const char *arith_buf; + +int +yylex() +{ + int value; + const char *buf = arith_buf; + const char *p; + + for (;;) { + value = *buf; + switch (value) { + case ' ': + case '\t': + case '\n': + buf++; + continue; + default: + return ARITH_BAD; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + yylval.val = strtoimax(buf, (char **)&arith_buf, 0); + return ARITH_NUM; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + p = buf; + while (buf++, is_in_name(*buf)) + ; + yylval.name = stalloc(buf - p + 1); + *(char *)mempcpy(yylval.name, p, buf - p) = 0; + value = ARITH_VAR; + goto out; + case '=': + value += ARITH_ASS - '='; +checkeq: + buf++; +checkeqcur: + if (*buf != '=') + goto out; + value += 11; + break; + case '>': + switch (*++buf) { + case '=': + value += ARITH_GE - '>'; + break; + case '>': + value += ARITH_RSHIFT - '>'; + goto checkeq; + default: + value += ARITH_GT - '>'; + goto out; + } + break; + case '<': + switch (*++buf) { + case '=': + value += ARITH_LE - '<'; + break; + case '<': + value += ARITH_LSHIFT - '<'; + goto checkeq; + default: + value += ARITH_LT - '<'; + goto out; + } + break; + case '|': + if (*++buf != '|') { + value += ARITH_BOR - '|'; + goto checkeqcur; + } + value += ARITH_OR - '|'; + break; + case '&': + if (*++buf != '&') { + value += ARITH_BAND - '&'; + goto checkeqcur; + } + value += ARITH_AND - '&'; + break; + case '!': + if (*++buf != '=') { + value += ARITH_NOT - '!'; + goto out; + } + value += ARITH_NE - '!'; + break; + case 0: + goto out; + case '(': + value += ARITH_LPAREN - '('; + break; + case ')': + value += ARITH_RPAREN - ')'; + break; + case '*': + value += ARITH_MUL - '*'; + goto checkeq; + case '/': + value += ARITH_DIV - '/'; + goto checkeq; + case '%': + value += ARITH_REM - '%'; + goto checkeq; + case '+': + value += ARITH_ADD - '+'; + goto checkeq; + case '-': + value += ARITH_SUB - '-'; + goto checkeq; + case '~': + value += ARITH_BNOT - '~'; + break; + case '^': + value += ARITH_BXOR - '^'; + goto checkeq; + case '?': + value += ARITH_QMARK - '?'; + break; + case ':': + value += ARITH_COLON - ':'; + break; + } + break; + } + + buf++; +out: + arith_buf = buf; + return value; +} diff --git a/usr/dash/bltin/bltin.h b/usr/dash/bltin/bltin.h new file mode 100644 index 0000000..f5ac06f --- /dev/null +++ b/usr/dash/bltin/bltin.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)bltin.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * This file is included by programs which are optionally built into the + * shell. If SHELL is defined, we try to map the standard UNIX library + * routines to ash routines using defines. + */ + +#include "../shell.h" +#include "../mystring.h" +#include "../options.h" +#ifdef SHELL +#include "../memalloc.h" +#include "../output.h" +#include "../error.h" +#ifndef USE_GLIBC_STDIO +#undef stdout +#undef stderr +#undef putc +#undef putchar +#undef fileno +#define stdout out1 +#define stderr out2 +#define printf out1fmt +#define putc(c, file) outc(c, file) +#define putchar(c) out1c(c) +#define FILE struct output +#define fprintf outfmt +#define fputs outstr +#define fflush flushout +#define fileno(f) ((f)->fd) +#define ferror outerr +#endif +#define INITARGS(argv) +#define error sh_error +#define warn sh_warn +#define warnx sh_warnx +#define exit sh_exit +#define setprogname(s) +#define getprogname() commandname +#define setlocate(l,s) 0 + +#define getenv(p) bltinlookup((p),0) + +#else +#undef NULL +#include <stdio.h> +#undef main +#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else +#endif + +int echocmd(int, char **); + + +extern const char *commandname; diff --git a/usr/dash/bltin/echo.1 b/usr/dash/bltin/echo.1 new file mode 100644 index 0000000..fbc7fb4 --- /dev/null +++ b/usr/dash/bltin/echo.1 @@ -0,0 +1,109 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 1997-2005 +.\" Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" Copyright 1989 by Kenneth Almquist +.\" +.\" 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. +.\" +.\" @(#)echo.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt ECHO 1 +.Os +.Sh NAME +.Nm echo +.Nd produce message in a shell script +.Sh SYNOPSIS +.Nm +.Op Fl n | Fl e +.Ar args ... +.Sh DESCRIPTION +.Nm +prints its arguments on the standard output, separated by spaces. +Unless the +.Fl n +option is present, a newline is output following the arguments. +The +.Fl e +option causes +.Nm +to treat the escape sequences specially, as described in the following +paragraph. +The +.Fl e +option is the default, and is provided solely for compatibility with +other systems. +Only one of the options +.Fl n +and +.Fl e +may be given. +.Pp +If any of the following sequences of characters is encountered during +output, the sequence is not output. Instead, the specified action is +performed: +.Bl -tag -width indent +.It Li \eb +A backspace character is output. +.It Li \ec +Subsequent output is suppressed. This is normally used at the end of the +last argument to suppress the trailing newline that +.Nm +would otherwise output. +.It Li \ef +Output a form feed. +.It Li \en +Output a newline character. +.It Li \er +Output a carriage return. +.It Li \et +Output a (horizontal) tab character. +.It Li \ev +Output a vertical tab. +.It Li \e0 Ns Ar digits +Output the character whose value is given by zero to three digits. +If there are zero digits, a nul character is output. +.It Li \e\e +Output a backslash. +.El +.Sh HINTS +Remember that backslash is special to the shell and needs to be escaped. +To output a message to standard error, say +.Pp +.D1 echo message \*[Gt]\*[Am]2 +.Sh BUGS +The octal character escape mechanism +.Pq Li \e0 Ns Ar digits +differs from the +C language mechanism. +.Pp +There is no way to force +.Nm +to treat its arguments literally, rather than interpreting them as +options and escape sequences. diff --git a/usr/dash/bltin/printf.1 b/usr/dash/bltin/printf.1 new file mode 100644 index 0000000..b1265a5 --- /dev/null +++ b/usr/dash/bltin/printf.1 @@ -0,0 +1,354 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 1997-2005 +.\" Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" from: @(#)printf.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd November 5, 1993 +.Dt PRINTF 1 +.Os +.Sh NAME +.Nm printf +.Nd formatted output +.Sh SYNOPSIS +.Nm +.Ar format +.Op Ar arguments ... +.Sh DESCRIPTION +.Nm +formats and prints its arguments, after the first, under control +of the +.Ar format . +The +.Ar format +is a character string which contains three types of objects: plain characters, +which are simply copied to standard output, character escape sequences which +are converted and copied to the standard output, and format specifications, +each of which causes printing of the next successive +.Ar argument . +.Pp +The +.Ar arguments +after the first are treated as strings if the corresponding format is +either +.Cm b , +.Cm B , +.Cm c +or +.Cm s ; +otherwise it is evaluated as a C constant, with the following extensions: +.Pp +.Bl -bullet -offset indent -compact +.It +A leading plus or minus sign is allowed. +.It +If the leading character is a single or double quote, the value is the +.Tn ASCII +code of the next character. +.El +.Pp +The format string is reused as often as necessary to satisfy the +.Ar arguments . +Any extra format specifications are evaluated with zero or the null +string. +.Pp +Character escape sequences are in backslash notation as defined in +.St -ansiC . +The characters and their meanings are as follows: +.Bl -tag -width Ds -offset indent +.It Cm \ee +Write an \*[Lt]escape\*[Gt] character. +.It Cm \ea +Write a \*[Lt]bell\*[Gt] character. +.It Cm \eb +Write a \*[Lt]backspace\*[Gt] character. +.It Cm \ef +Write a \*[Lt]form-feed\*[Gt] character. +.It Cm \en +Write a \*[Lt]new-line\*[Gt] character. +.It Cm \er +Write a \*[Lt]carriage return\*[Gt] character. +.It Cm \et +Write a \*[Lt]tab\*[Gt] character. +.It Cm \ev +Write a \*[Lt]vertical tab\*[Gt] character. +.It Cm \e\' +Write a \*[Lt]single quote\*[Gt] character. +.It Cm \e" +Write a \*[Lt]double quote\*[Gt] character. +.It Cm \e\e +Write a backslash character. +.It Cm \e Ns Ar num +Write an 8\-bit character whose +.Tn ASCII +value is the 1\-, 2\-, or 3\-digit octal number +.Ar num . +.It Cm \ex Ns Ar xx +Write an 8\-bit character whose +.Tn ASCII +value is the 1\- or 2\-digit hexadecimal number +.Ar xx . +.El +.Pp +Each format specification is introduced by the percent character +(``%''). +The remainder of the format specification includes, +in the following order: +.Bl -tag -width Ds +.It "Zero or more of the following flags:" +.Bl -tag -width Ds +.It Cm # +A `#' character +specifying that the value should be printed in an ``alternative form''. +For +.Cm b , +.Cm c , +.Cm d , +and +.Cm s +formats, this option has no effect. +For the +.Cm o +format the precision of the number is increased to force the first +character of the output string to a zero. +For the +.Cm x +.Pq Cm X +format, a non-zero result has the string +.Li 0x +.Pq Li 0X +prepended to it. +For +.Cm e , +.Cm E , +.Cm f , +.Cm g , +and +.Cm G +formats, the result will always contain a decimal point, even if no +digits follow the point (normally, a decimal point only appears in the +results of those formats if a digit follows the decimal point). +For +.Cm g +and +.Cm G +formats, trailing zeros are not removed from the result as they +would otherwise be. +.\" I turned this off - decided it isn't a valid use of '#' +.\" For the +.\" .Cm B +.\" format, backslash-escape sequences are expanded first; +.It Cm \&\- +A minus sign `\-' which specifies +.Em left adjustment +of the output in the indicated field; +.It Cm \&+ +A `+' character specifying that there should always be +a sign placed before the number when using signed formats. +.It Sq \&\ \& +A space specifying that a blank should be left before a positive number +for a signed format. +A `+' overrides a space if both are used; +.It Cm \&0 +A zero `0' character indicating that zero-padding should be used +rather than blank-padding. +A `\-' overrides a `0' if both are used; +.El +.It "Field Width:" +An optional digit string specifying a +.Em field width ; +if the output string has fewer characters than the field width it will +be blank-padded on the left (or right, if the left-adjustment indicator +has been given) to make up the field width (note that a leading zero +is a flag, but an embedded zero is part of a field width); +.It Precision : +An optional period, +.Sq Cm \&.\& , +followed by an optional digit string giving a +.Em precision +which specifies the number of digits to appear after the decimal point, +for +.Cm e +and +.Cm f +formats, or the maximum number of characters to be printed +from a string +.Sm off +.Pf ( Cm b No , +.Sm on +.Cm B +and +.Cm s +formats); if the digit string is missing, the precision is treated +as zero; +.It Format : +A character which indicates the type of format to use (one of +.Cm diouxXfwEgGbBcs ) . +.El +.Pp +A field width or precision may be +.Sq Cm \&* +instead of a digit string. +In this case an +.Ar argument +supplies the field width or precision. +.Pp +The format characters and their meanings are: +.Bl -tag -width Fl +.It Cm diouXx +The +.Ar argument +is printed as a signed decimal (d or i), unsigned octal, unsigned decimal, +or unsigned hexadecimal (X or x), respectively. +.It Cm f +The +.Ar argument +is printed in the style +.Sm off +.Pf [\-]ddd Cm \&. No ddd +.Sm on +where the number of d's +after the decimal point is equal to the precision specification for +the argument. +If the precision is missing, 6 digits are given; if the precision +is explicitly 0, no digits and no decimal point are printed. +.It Cm eE +The +.Ar argument +is printed in the style +.Sm off +.Pf [\-]d Cm \&. No ddd Cm e No \\*(Pmdd +.Sm on +where there +is one digit before the decimal point and the number after is equal to +the precision specification for the argument; when the precision is +missing, 6 digits are produced. +An upper-case E is used for an `E' format. +.It Cm gG +The +.Ar argument +is printed in style +.Cm f +or in style +.Cm e +.Pq Cm E +whichever gives full precision in minimum space. +.It Cm b +Characters from the string +.Ar argument +are printed with backslash-escape sequences expanded. +.br +The following additional backslash-escape sequences are supported: +.Bl -tag -width Ds +.It Cm \ec +Causes +.Nm +to ignore any remaining characters in the string operand containing it, +any remaining string operands, and any additional characters in +the format operand. +.It Cm \e0 Ns Ar num +Write an 8\-bit character whose +.Tn ASCII +value is the 1\-, 2\-, or 3\-digit +octal number +.Ar num . +.It Cm \e^ Ns Ar c +Write the control character +.Ar c . +Generates characters `\e000' through `\e037`, and `\e177' (from `\e^?'). +.It Cm \eM\- Ns Ar c +Write the character +.Ar c +with the 8th bit set. +Generates characters `\e241' through `\e376`. +.It Cm \eM^ Ns Ar c +Write the control character +.Ar c +with the 8th bit set. +Generates characters `\e000' through `\e037`, and `\e177' (from `\eM^?'). +.El +.It Cm B +Characters from the string +.Ar argument +are printed with unprintable characters backslash-escaped using the +.Sm off +.Pf ` Cm \e Ar c No ', +.Pf ` Cm \e^ Ar c No ', +.Pf ` Cm \eM\- Ar c No ' +or +.Pf ` Cm \eM^ Ar c No ', +.Sm on +formats described above. +.It Cm c +The first character of +.Ar argument +is printed. +.It Cm s +Characters from the string +.Ar argument +are printed until the end is reached or until the number of characters +indicated by the precision specification is reached; if the +precision is omitted, all characters in the string are printed. +.It Cm \&% +Print a `%'; no argument is used. +.El +.Pp +In no case does a non-existent or small field width cause truncation of +a field; padding takes place only if the specified field width exceeds +the actual width. +.Sh EXIT STATUS +.Nm +exits 0 on success, 1 on failure. +.Sh SEE ALSO +.Xr echo 1 , +.Xr printf 3 , +.Xr printf 9 +.Xr vis 3 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 . +.Pp +Support for the floating point formats and `*' as a field width and precision +are optional in POSIX. +.Pp +The behaviour of the %B format and the \e', \e", \exxx, \ee and +\e[M][\-|^]c escape sequences are undefined in POSIX. +.Sh BUGS +Since the floating point numbers are translated from +.Tn ASCII +to floating-point and +then back again, floating-point precision may be lost. +.Pp +Hexadecimal character constants are restricted to, and should be specified +as, two character constants. This is contrary to the ISO C standard but +does guarantee detection of the end of the constant. diff --git a/usr/dash/bltin/printf.c b/usr/dash/bltin/printf.c new file mode 100644 index 0000000..b32b54f --- /dev/null +++ b/usr/dash/bltin/printf.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. 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. + * 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. + */ + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static int conv_escape_str(char *); +static char *conv_escape(char *, int *); +static int getchr(void); +static intmax_t getintmax(void); +static uintmax_t getuintmax(void); +static char *getstr(void); +static char *mklong(const char *, const char *); +static void check_conversion(const char *, const char *); +#ifdef HAVE_STRTOD +static double getdouble(void); +#endif + +static int rval; +static char **gargv; + +#define isodigit(c) ((c) >= '0' && (c) <= '7') +#define octtobin(c) ((c) - '0') + +#include "bltin.h" +#include "system.h" + +#define PF(f, func) { \ + switch ((char *)param - (char *)array) { \ + default: \ + (void)printf(f, array[0], array[1], func); \ + break; \ + case sizeof(*param): \ + (void)printf(f, array[0], func); \ + break; \ + case 0: \ + (void)printf(f, func); \ + break; \ + } \ +} + +int printfcmd(int argc, char *argv[]) +{ + char *fmt; + char *format; + int ch; + + rval = 0; + + nextopt(nullstr); + + argv = argptr; + format = *argv; + + if (!format) { + warnx("usage: printf format [arg ...]"); + goto err; + } + + gargv = ++argv; + +#define SKIP1 "#-+ 0" +#define SKIP2 "*0123456789" + do { + /* + * Basic algorithm is to scan the format string for conversion + * specifications -- once one is found, find out if the field + * width or precision is a '*'; if it is, gather up value. + * Note, format strings are reused as necessary to use up the + * provided arguments, arguments of zero/null string are + * provided to use up the format string. + */ + + /* find next format specification */ + for (fmt = format; (ch = *fmt++) ;) { + char *start; + char nextch; + int array[2]; + int *param; + + if (ch == '\\') { + int c_ch; + fmt = conv_escape(fmt, &c_ch); + ch = c_ch; + goto pc; + } + if (ch != '%' || (*fmt == '%' && (++fmt || 1))) { +pc: + putchar(ch); + continue; + } + + /* Ok - we've found a format specification, + Save its address for a later printf(). */ + start = fmt - 1; + param = array; + + /* skip to field width */ + fmt += strspn(fmt, SKIP1); + if (*fmt == '*') + *param++ = getintmax(); + + /* skip to possible '.', get following precision */ + fmt += strspn(fmt, SKIP2); + if (*fmt == '.') + ++fmt; + if (*fmt == '*') + *param++ = getintmax(); + + fmt += strspn(fmt, SKIP2); + + ch = *fmt; + if (!ch) { + warnx("missing format character"); + goto err; + } + /* null terminate format string to we can use it + as an argument to printf. */ + nextch = fmt[1]; + fmt[1] = 0; + switch (ch) { + + case 'b': { + int done = conv_escape_str(getstr()); + char *p = stackblock(); + *fmt = 's'; + PF(start, p); + /* escape if a \c was encountered */ + if (done) + goto out; + *fmt = 'b'; + break; + } + case 'c': { + int p = getchr(); + PF(start, p); + break; + } + case 's': { + char *p = getstr(); + PF(start, p); + break; + } + case 'd': + case 'i': { + intmax_t p = getintmax(); + char *f = mklong(start, fmt); + PF(f, p); + break; + } + case 'o': + case 'u': + case 'x': + case 'X': { + uintmax_t p = getuintmax(); + char *f = mklong(start, fmt); + PF(f, p); + break; + } +#ifdef HAVE_STRTOD + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': { + double p = getdouble(); + PF(start, p); + break; + } +#endif + default: + warnx("%s: invalid directive", start); + goto err; + } + *++fmt = nextch; + } + } while (gargv != argv && *gargv); + +out: + return rval; +err: + return 1; +} + + +/* + * Print SysV echo(1) style escape string + * Halts processing string if a \c escape is encountered. + */ +static int +conv_escape_str(char *str) +{ + int ch; + char *cp; + + /* convert string into a temporary buffer... */ + STARTSTACKSTR(cp); + + do { + int c; + + ch = *str++; + if (ch != '\\') + continue; + + ch = *str++; + if (ch == 'c') { + /* \c as in SYSV echo - abort all processing.... */ + ch = 0x100; + continue; + } + + /* + * %b string octal constants are not like those in C. + * They start with a \0, and are followed by 0, 1, 2, + * or 3 octal digits. + */ + if (ch == '0') { + unsigned char i; + i = 3; + ch = 0; + do { + unsigned k = octtobin(*str); + if (k > 7) + break; + str++; + ch <<= 3; + ch += k; + } while (--i); + continue; + } + + /* Finally test for sequences valid in the format string */ + str = conv_escape(str - 1, &c); + ch = c; + } while (STPUTC(ch, cp), (char)ch); + + return ch; +} + +/* + * Print "standard" escape characters + */ +static char * +conv_escape(char *str, int *conv_ch) +{ + int value; + int ch; + + ch = *str; + + switch (ch) { + default: + case 0: + value = '\\'; + goto out; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + ch = 3; + value = 0; + do { + value <<= 3; + value += octtobin(*str++); + } while (isodigit(*str) && --ch); + goto out; + + case '\\': value = '\\'; break; /* backslash */ + case 'a': value = '\a'; break; /* alert */ + case 'b': value = '\b'; break; /* backspace */ + case 'f': value = '\f'; break; /* form-feed */ + case 'n': value = '\n'; break; /* newline */ + case 'r': value = '\r'; break; /* carriage-return */ + case 't': value = '\t'; break; /* tab */ + case 'v': value = '\v'; break; /* vertical-tab */ + } + + str++; +out: + *conv_ch = value; + return str; +} + +static char * +mklong(const char *str, const char *ch) +{ + char *copy; + size_t len; + + len = ch - str + 3; + STARTSTACKSTR(copy); + copy = makestrspace(len, copy); + memcpy(copy, str, len - 3); + copy[len - 3] = 'j'; + copy[len - 2] = *ch; + copy[len - 1] = '\0'; + return (copy); +} + +static int +getchr(void) +{ + int val = 0; + + if (*gargv) + val = **gargv++; + return val; +} + +static char * +getstr(void) +{ + char *val = nullstr; + + if (*gargv) + val = *gargv++; + return val; +} + +static intmax_t +getintmax(void) +{ + intmax_t val = 0; + char *cp, *ep; + + cp = *gargv; + if (cp == NULL) + goto out; + gargv++; + + val = (unsigned char) cp[1]; + if (*cp == '\"' || *cp == '\'') + goto out; + + errno = 0; + val = strtoimax(cp, &ep, 0); + check_conversion(cp, ep); +out: + return val; +} + +static uintmax_t +getuintmax(void) +{ + uintmax_t val = 0; + char *cp, *ep; + + cp = *gargv; + if (cp == NULL) + goto out; + gargv++; + + val = (unsigned char) cp[1]; + if (*cp == '\"' || *cp == '\'') + goto out; + + errno = 0; + val = strtoumax(cp, &ep, 0); + check_conversion(cp, ep); +out: + return val; +} + +#ifdef HAVE_STRTOD +static double +getdouble(void) +{ + double val; + char *cp, *ep; + + cp = *gargv; + if (cp == NULL) + return 0; + gargv++; + + if (*cp == '\"' || *cp == '\'') + return (unsigned char) cp[1]; + + errno = 0; + val = strtod(cp, &ep); + check_conversion(cp, ep); + return val; +} +#endif + +static void +check_conversion(const char *s, const char *ep) +{ + if (*ep) { + if (ep == s) + warnx("%s: expected numeric value", s); + else + warnx("%s: not completely converted", s); + rval = 1; + } else if (errno == ERANGE) { + warnx("%s: %s", s, strerror(ERANGE)); + rval = 1; + } +} + +int +echocmd(int argc, char **argv) +{ + int nonl = 0; + struct output *outs = out1; + + if (!*++argv) + goto end; + if (equal(*argv, "-n")) { + nonl = ~nonl; + if (!*++argv) + goto end; + } + + do { + int c; + + nonl += conv_escape_str(*argv); + outstr(stackblock(), outs); + if (nonl > 0) + break; + + c = ' '; + if (!*++argv) { +end: + if (nonl) { + break; + } + c = '\n'; + } + outc(c, outs); + } while (*argv); + return 0; +} diff --git a/usr/dash/bltin/test.1 b/usr/dash/bltin/test.1 new file mode 100644 index 0000000..42435fb --- /dev/null +++ b/usr/dash/bltin/test.1 @@ -0,0 +1,309 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 1997-2005 +.\" Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)test.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt TEST 1 +.Os +.Sh NAME +.Nm test , +.Nm \&[ +.Nd condition evaluation utility +.Sh SYNOPSIS +.Nm test +.Ar expression +.Nm \&[ +.Ar expression Cm ] +.Sh DESCRIPTION +The +.Nm test +utility evaluates the expression and, if it evaluates +to true, returns a zero (true) exit status; otherwise +it returns 1 (false). +If there is no expression, test also +returns 1 (false). +.Pp +All operators and flags are separate arguments to the +.Nm test +utility. +.Pp +The following primaries are used to construct expression: +.Bl -tag -width Ar +.It Fl b Ar file +True if +.Ar file +exists and is a block special +file. +.It Fl c Ar file +True if +.Ar file +exists and is a character +special file. +.It Fl d Ar file +True if +.Ar file +exists and is a directory. +.It Fl e Ar file +True if +.Ar file +exists (regardless of type). +.It Fl f Ar file +True if +.Ar file +exists and is a regular file. +.It Fl g Ar file +True if +.Ar file +exists and its set group ID flag +is set. +.It Fl h Ar file +True if +.Ar file +exists and is a symbolic link. +.It Fl k Ar file +True if +.Ar file +exists and its sticky bit is set. +.It Fl n Ar string +True if the length of +.Ar string +is nonzero. +.It Fl p Ar file +True if +.Ar file +is a named pipe +.Po Tn FIFO Pc . +.It Fl r Ar file +True if +.Ar file +exists and is readable. +.It Fl s Ar file +True if +.Ar file +exists and has a size greater +than zero. +.It Fl t Ar file_descriptor +True if the file whose file descriptor number +is +.Ar file_descriptor +is open and is associated with a terminal. +.It Fl u Ar file +True if +.Ar file +exists and its set user ID flag +is set. +.It Fl w Ar file +True if +.Ar file +exists and is writable. +True +indicates only that the write flag is on. +The file is not writable on a read-only file +system even if this test indicates true. +.It Fl x Ar file +True if +.Ar file +exists and is executable. +True +indicates only that the execute flag is on. +If +.Ar file +is a directory, true indicates that +.Ar file +can be searched. +.It Fl z Ar string +True if the length of +.Ar string +is zero. +.It Fl L Ar file +True if +.Ar file +exists and is a symbolic link. +This operator is retained for compatibility with previous versions of +this program. +Do not rely on its existence; use +.Fl h +instead. +.It Fl O Ar file +True if +.Ar file +exists and its owner matches the effective user id of this process. +.It Fl G Ar file +True if +.Ar file +exists and its group matches the effective group id of this process. +.It Fl S Ar file +True if +.Ar file +exists and is a socket. +.It Ar file1 Fl nt Ar file2 +True if +.Ar file1 +exists and is newer than +.Ar file2 . +.It Ar file1 Fl ot Ar file2 +True if +.Ar file1 +exists and is older than +.Ar file2 . +.It Ar file1 Fl ef Ar file2 +True if +.Ar file1 +and +.Ar file2 +exist and refer to the same file. +.It Ar string +True if +.Ar string +is not the null +string. +.It Ar \&s\&1 Cm \&= Ar \&s\&2 +True if the strings +.Ar \&s\&1 +and +.Ar \&s\&2 +are identical. +.It Ar \&s\&1 Cm \&!= Ar \&s\&2 +True if the strings +.Ar \&s\&1 +and +.Ar \&s\&2 +are not identical. +.It Ar \&s\&1 Cm \&\*[Lt] Ar \&s\&2 +True if string +.Ar \&s\&1 +comes before +.Ar \&s\&2 +based on the ASCII value of their characters. +.It Ar \&s\&1 Cm \&\*[Gt] Ar \&s\&2 +True if string +.Ar \&s\&1 +comes after +.Ar \&s\&2 +based on the ASCII value of their characters. +.It Ar \&n\&1 Fl \&eq Ar \&n\&2 +True if the integers +.Ar \&n\&1 +and +.Ar \&n\&2 +are algebraically +equal. +.It Ar \&n\&1 Fl \&ne Ar \&n\&2 +True if the integers +.Ar \&n\&1 +and +.Ar \&n\&2 +are not +algebraically equal. +.It Ar \&n\&1 Fl \> Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically +greater than the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \&ge Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically +greater than or equal to the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \< Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically less +than the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \&le Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically less +than or equal to the integer +.Ar \&n\&2 . +.El +.Pp +These primaries can be combined with the following operators: +.Bl -tag -width Ar +.It Cm \&! Ar expression +True if +.Ar expression +is false. +.It Ar expression1 Fl a Ar expression2 +True if both +.Ar expression1 +and +.Ar expression2 +are true. +.It Ar expression1 Fl o Ar expression2 +True if either +.Ar expression1 +or +.Ar expression2 +are true. +.It Cm \&( Ns Ar expression Ns Cm \&) +True if expression is true. +.El +.Pp +The +.Fl a +operator has higher precedence than the +.Fl o +operator. +.Sh GRAMMAR AMBIGUITY +The +.Nm test +grammar is inherently ambiguous. +In order to assure a degree of consistency, the cases described in +.St -p1003.2 +section 4.62.4, +are evaluated consistently according to the rules specified in the +standards document. +All other cases are subject to the ambiguity in the command semantics. +.Sh EXIT STATUS +The +.Nm test +utility exits with one of the following values: +.Bl -tag -width Ds +.It 0 +expression evaluated to true. +.It 1 +expression evaluated to false or expression was +missing. +.It \*[Gt]1 +An error occurred. +.El +.Sh STANDARDS +The +.Nm test +utility implements a superset of the +.St -p1003.2 +specification. diff --git a/usr/dash/bltin/test.c b/usr/dash/bltin/test.c new file mode 100644 index 0000000..90135e1 --- /dev/null +++ b/usr/dash/bltin/test.c @@ -0,0 +1,543 @@ +/* + * test(1); version 7-like -- author Erik Baalbergen + * modified by Eric Gisin to be used as built-in. + * modified by Arnold Robbins to add SVR3 compatibility + * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). + * modified by J.T. Conklin for NetBSD. + * + * This program is in the Public Domain. + */ + +#include <sys/stat.h> +#include <sys/types.h> + +#include <fcntl.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include "bltin.h" + +/* test(1) accepts the following grammar: + oexpr ::= aexpr | aexpr "-o" oexpr ; + aexpr ::= nexpr | nexpr "-a" aexpr ; + nexpr ::= primary | "!" primary + primary ::= unary-operator operand + | operand binary-operator operand + | operand + | "(" oexpr ")" + ; + unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| + "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; + + binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| + "-nt"|"-ot"|"-ef"; + operand ::= <any legal UNIX file name> +*/ + +enum token { + EOI, + FILRD, + FILWR, + FILEX, + FILEXIST, + FILREG, + FILDIR, + FILCDEV, + FILBDEV, + FILFIFO, + FILSOCK, + FILSYM, + FILGZ, + FILTT, + FILSUID, + FILSGID, + FILSTCK, + FILNT, + FILOT, + FILEQ, + FILUID, + FILGID, + STREZ, + STRNZ, + STREQ, + STRNE, + STRLT, + STRGT, + INTEQ, + INTNE, + INTGE, + INTGT, + INTLE, + INTLT, + UNOT, + BAND, + BOR, + LPAREN, + RPAREN, + OPERAND +}; + +enum token_types { + UNOP, + BINOP, + BUNOP, + BBINOP, + PAREN +}; + +static struct t_op { + const char *op_text; + short op_num, op_type; +} const ops [] = { + {"-r", FILRD, UNOP}, + {"-w", FILWR, UNOP}, + {"-x", FILEX, UNOP}, + {"-e", FILEXIST,UNOP}, + {"-f", FILREG, UNOP}, + {"-d", FILDIR, UNOP}, + {"-c", FILCDEV,UNOP}, + {"-b", FILBDEV,UNOP}, + {"-p", FILFIFO,UNOP}, + {"-u", FILSUID,UNOP}, + {"-g", FILSGID,UNOP}, + {"-k", FILSTCK,UNOP}, + {"-s", FILGZ, UNOP}, + {"-t", FILTT, UNOP}, + {"-z", STREZ, UNOP}, + {"-n", STRNZ, UNOP}, + {"-h", FILSYM, UNOP}, /* for backwards compat */ + {"-O", FILUID, UNOP}, + {"-G", FILGID, UNOP}, + {"-L", FILSYM, UNOP}, + {"-S", FILSOCK,UNOP}, + {"=", STREQ, BINOP}, + {"!=", STRNE, BINOP}, + {"<", STRLT, BINOP}, + {">", STRGT, BINOP}, + {"-eq", INTEQ, BINOP}, + {"-ne", INTNE, BINOP}, + {"-ge", INTGE, BINOP}, + {"-gt", INTGT, BINOP}, + {"-le", INTLE, BINOP}, + {"-lt", INTLT, BINOP}, + {"-nt", FILNT, BINOP}, + {"-ot", FILOT, BINOP}, + {"-ef", FILEQ, BINOP}, + {"!", UNOT, BUNOP}, + {"-a", BAND, BBINOP}, + {"-o", BOR, BBINOP}, + {"(", LPAREN, PAREN}, + {")", RPAREN, PAREN}, + {0, 0, 0} +}; + +static char **t_wp; +static struct t_op const *t_wp_op; + +static void syntax(const char *, const char *); +static int oexpr(enum token); +static int aexpr(enum token); +static int nexpr(enum token); +static int primary(enum token); +static int binop(void); +static int filstat(char *, enum token); +static enum token t_lex(char **); +static int isoperand(char **); +static int newerf(const char *, const char *); +static int olderf(const char *, const char *); +static int equalf(const char *, const char *); +#ifdef HAVE_FACCESSAT +static int test_file_access(const char *, int); +#else +static int test_st_mode(const struct stat64 *, int); +static int bash_group_member(gid_t); +#endif + +static inline intmax_t getn(const char *s) +{ + return atomax10(s); +} + +static const struct t_op *getop(const char *s) +{ + const struct t_op *op; + + for (op = ops; op->op_text; op++) { + if (strcmp(s, op->op_text) == 0) + return op; + } + + return NULL; +} + +int +testcmd(int argc, char **argv) +{ + const struct t_op *op; + enum token n; + int res; + + if (*argv[0] == '[') { + if (*argv[--argc] != ']') + error("missing ]"); + argv[argc] = NULL; + } + + argv++; + argc--; + + if (argc < 1) + return 1; + + /* + * POSIX prescriptions: he who wrote this deserves the Nobel + * peace prize. + */ + switch (argc) { + case 3: + op = getop(argv[1]); + if (op && op->op_type == BINOP) { + n = OPERAND; + goto eval; + } + /* fall through */ + + case 4: + if (!strcmp(argv[0], "(") && !strcmp(argv[argc - 1], ")")) { + argv[--argc] = NULL; + argv++; + argc--; + } + } + + n = t_lex(argv); + +eval: + t_wp = argv; + res = !oexpr(n); + argv = t_wp; + + if (argv[0] != NULL && argv[1] != NULL) + syntax(argv[0], "unexpected operator"); + + return res; +} + +static void +syntax(const char *op, const char *msg) +{ + if (op && *op) + error("%s: %s", op, msg); + else + error("%s", msg); +} + +static int +oexpr(enum token n) +{ + int res = 0; + + for (;;) { + res |= aexpr(n); + n = t_lex(t_wp + 1); + if (n != BOR) + break; + n = t_lex(t_wp += 2); + } + return res; +} + +static int +aexpr(enum token n) +{ + int res = 1; + + for (;;) { + if (!nexpr(n)) + res = 0; + n = t_lex(t_wp + 1); + if (n != BAND) + break; + n = t_lex(t_wp += 2); + } + return res; +} + +static int +nexpr(enum token n) +{ + if (n == UNOT) + return !nexpr(t_lex(++t_wp)); + return primary(n); +} + +static int +primary(enum token n) +{ + enum token nn; + int res; + + if (n == EOI) + return 0; /* missing expression */ + if (n == LPAREN) { + if ((nn = t_lex(++t_wp)) == RPAREN) + return 0; /* missing expression */ + res = oexpr(nn); + if (t_lex(++t_wp) != RPAREN) + syntax(NULL, "closing paren expected"); + return res; + } + if (t_wp_op && t_wp_op->op_type == UNOP) { + /* unary expression */ + if (*++t_wp == NULL) + syntax(t_wp_op->op_text, "argument expected"); + switch (n) { + case STREZ: + return strlen(*t_wp) == 0; + case STRNZ: + return strlen(*t_wp) != 0; + case FILTT: + return isatty(getn(*t_wp)); +#ifdef HAVE_FACCESSAT + case FILRD: + return test_file_access(*t_wp, R_OK); + case FILWR: + return test_file_access(*t_wp, W_OK); + case FILEX: + return test_file_access(*t_wp, X_OK); +#endif + default: + return filstat(*t_wp, n); + } + } + + if (t_lex(t_wp + 1), t_wp_op && t_wp_op->op_type == BINOP) { + return binop(); + } + + return strlen(*t_wp) > 0; +} + +static int +binop(void) +{ + const char *opnd1, *opnd2; + struct t_op const *op; + + opnd1 = *t_wp; + (void) t_lex(++t_wp); + op = t_wp_op; + + if ((opnd2 = *++t_wp) == (char *)0) + syntax(op->op_text, "argument expected"); + + switch (op->op_num) { + default: +#ifdef DEBUG + abort(); + /* NOTREACHED */ +#endif + case STREQ: + return strcmp(opnd1, opnd2) == 0; + case STRNE: + return strcmp(opnd1, opnd2) != 0; + case STRLT: + return strcmp(opnd1, opnd2) < 0; + case STRGT: + return strcmp(opnd1, opnd2) > 0; + case INTEQ: + return getn(opnd1) == getn(opnd2); + case INTNE: + return getn(opnd1) != getn(opnd2); + case INTGE: + return getn(opnd1) >= getn(opnd2); + case INTGT: + return getn(opnd1) > getn(opnd2); + case INTLE: + return getn(opnd1) <= getn(opnd2); + case INTLT: + return getn(opnd1) < getn(opnd2); + case FILNT: + return newerf (opnd1, opnd2); + case FILOT: + return olderf (opnd1, opnd2); + case FILEQ: + return equalf (opnd1, opnd2); + } +} + +static int +filstat(char *nm, enum token mode) +{ + struct stat64 s; + + if (mode == FILSYM ? lstat64(nm, &s) : stat64(nm, &s)) + return 0; + + switch (mode) { +#ifndef HAVE_FACCESSAT + case FILRD: + return test_st_mode(&s, R_OK); + case FILWR: + return test_st_mode(&s, W_OK); + case FILEX: + return test_st_mode(&s, X_OK); +#endif + case FILEXIST: + return 1; + case FILREG: + return S_ISREG(s.st_mode); + case FILDIR: + return S_ISDIR(s.st_mode); + case FILCDEV: + return S_ISCHR(s.st_mode); + case FILBDEV: + return S_ISBLK(s.st_mode); + case FILFIFO: + return S_ISFIFO(s.st_mode); + case FILSOCK: + return S_ISSOCK(s.st_mode); + case FILSYM: + return S_ISLNK(s.st_mode); + case FILSUID: + return (s.st_mode & S_ISUID) != 0; + case FILSGID: + return (s.st_mode & S_ISGID) != 0; + case FILSTCK: + return (s.st_mode & S_ISVTX) != 0; + case FILGZ: + return !!s.st_size; + case FILUID: + return s.st_uid == geteuid(); + case FILGID: + return s.st_gid == getegid(); + default: + return 1; + } +} + +static enum token t_lex(char **tp) +{ + struct t_op const *op; + char *s = *tp; + + if (s == 0) { + t_wp_op = (struct t_op *)0; + return EOI; + } + + op = getop(s); + if (op && !(op->op_type == UNOP && isoperand(tp)) && + !(op->op_num == LPAREN && !tp[1])) { + t_wp_op = op; + return op->op_num; + } + + t_wp_op = (struct t_op *)0; + return OPERAND; +} + +static int isoperand(char **tp) +{ + struct t_op const *op; + char *s; + + if (!(s = tp[1])) + return 1; + if (!tp[2]) + return 0; + + op = getop(s); + return op && op->op_type == BINOP; +} + +static int +newerf (const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_mtime > b2.st_mtime); +} + +static int +olderf (const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_mtime < b2.st_mtime); +} + +static int +equalf (const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_dev == b2.st_dev && + b1.st_ino == b2.st_ino); +} + +#ifdef HAVE_FACCESSAT +static int test_file_access(const char *path, int mode) +{ + return !faccessat(AT_FDCWD, path, mode, AT_EACCESS); +} +#else /* HAVE_FACCESSAT */ +/* + * Similar to what access(2) does, but uses the effective uid and gid. + * Doesn't make the mistake of telling root that any file is executable. + * Returns non-zero if the file is accessible. + */ +static int +test_st_mode(const struct stat64 *st, int mode) +{ + int euid = geteuid(); + + if (euid == 0) { + /* Root can read or write any file. */ + if (mode != X_OK) + return 1; + + /* Root can execute any file that has any one of the execute + bits set. */ + mode = S_IXUSR | S_IXGRP | S_IXOTH; + } else if (st->st_uid == euid) + mode <<= 6; + else if (bash_group_member(st->st_gid)) + mode <<= 3; + + return st->st_mode & mode; +} + +/* Return non-zero if GID is one that we have in our groups list. */ +static int +bash_group_member(gid_t gid) +{ + register int i; + gid_t *group_array; + int ngroups; + + /* Short-circuit if possible, maybe saving a call to getgroups(). */ + if (gid == getgid() || gid == getegid()) + return (1); + + ngroups = getgroups(0, NULL); + group_array = stalloc(ngroups * sizeof(gid_t)); + if ((getgroups(ngroups, group_array)) != ngroups) + return (0); + + /* Search through the list looking for GID. */ + for (i = 0; i < ngroups; i++) + if (gid == group_array[i]) + return (1); + + return (0); +} +#endif /* HAVE_FACCESSAT */ diff --git a/usr/dash/builtins.def.in b/usr/dash/builtins.def.in new file mode 100644 index 0000000..e116963 --- /dev/null +++ b/usr/dash/builtins.def.in @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)builtins.def 8.4 (Berkeley) 5/4/95 + */ + +/* + * This file lists all the builtin commands. The first column is the name + * of a C routine. + * The -a flag specifies that this is a posix 'assignment builtin' command. + * The -s flag specifies that this is a posix 'special builtin' command. + * The -u flag specifies that this is a posix 'standard utility'. + * The -n flag specifies that this command has a special entry point. + * The rest of the line specifies the command name or names used to run + * the command. + */ + +#ifndef JOBS +#define JOBS 1 +#endif + +#if JOBS +bgcmd -u bg +fgcmd -u fg +#endif + +#ifndef SMALL +histcmd -u fc +#endif + +breakcmd -s break -s continue +cdcmd -u cd chdir +commandcmd -u command +dotcmd -s . +echocmd echo +evalcmd -ns eval +execcmd -s exec +exitcmd -s exit +exportcmd -as export -as readonly +falsecmd -u false +getoptscmd -u getopts +hashcmd hash +jobscmd -u jobs +localcmd -as local +printfcmd printf +pwdcmd pwd +readcmd -u read +returncmd -s return +setcmd -s set +shiftcmd -s shift +trapcmd -s trap +truecmd -s : -u true +typecmd type +umaskcmd -u umask +unaliascmd -u unalias +unsetcmd -s unset +waitcmd -u wait +aliascmd -au alias +#ifdef HAVE_GETRLIMIT +ulimitcmd ulimit +#endif +testcmd test [ +killcmd -u kill diff --git a/usr/dash/cd.c b/usr/dash/cd.c new file mode 100644 index 0000000..89c6c30 --- /dev/null +++ b/usr/dash/cd.c @@ -0,0 +1,313 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +/* + * The cd and pwd commands. + */ + +#include "shell.h" +#include "var.h" +#include "nodes.h" /* for jobs.h */ +#include "jobs.h" +#include "options.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "exec.h" +#include "redir.h" +#include "main.h" +#include "mystring.h" +#include "show.h" +#include "cd.h" + +#define CD_PHYSICAL 1 +#define CD_PRINT 2 + +STATIC int docd(const char *, int); +STATIC const char *updatepwd(const char *); +STATIC char *getpwd(void); +STATIC int cdopt(void); + +STATIC char *curdir = nullstr; /* current working directory */ +STATIC char *physdir = nullstr; /* physical working directory */ + +STATIC int +cdopt() +{ + int flags = 0; + int i, j; + + j = 'L'; + while ((i = nextopt("LP"))) { + if (i != j) { + flags ^= CD_PHYSICAL; + j = i; + } + } + + return flags; +} + +int +cdcmd(int argc, char **argv) +{ + const char *dest; + const char *path; + const char *p; + char c; + struct stat statb; + int flags; + + flags = cdopt(); + dest = *argptr; + if (!dest) + dest = bltinlookup(homestr); + else if (dest[0] == '-' && dest[1] == '\0') { + dest = bltinlookup("OLDPWD"); + flags |= CD_PRINT; + } + if (!dest) + dest = nullstr; + if (*dest == '/') + goto step6; + if (*dest == '.') { + c = dest[1]; +dotdot: + switch (c) { + case '\0': + case '/': + goto step6; + case '.': + c = dest[2]; + if (c != '.') + goto dotdot; + } + } + if (!*dest) + dest = "."; + path = bltinlookup("CDPATH"); + while (path) { + c = *path; + p = padvance(&path, dest); + if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { + if (c && c != ':') + flags |= CD_PRINT; +docd: + if (!docd(p, flags)) + goto out; + goto err; + } + } + +step6: + p = dest; + goto docd; + +err: + sh_error("can't cd to %s", dest); + /* NOTREACHED */ +out: + if (flags & CD_PRINT) + out1fmt(snlfmt, curdir); + return 0; +} + + +/* + * Actually do the chdir. We also call hashcd to let the routines in exec.c + * know that the current directory has changed. + */ + +STATIC int +docd(const char *dest, int flags) +{ + const char *dir = 0; + int err; + + TRACE(("docd(\"%s\", %d) called\n", dest, flags)); + + INTOFF; + if (!(flags & CD_PHYSICAL)) { + dir = updatepwd(dest); + if (dir) + dest = dir; + } + err = chdir(dest); + if (err) + goto out; + setpwd(dir, 1); + hashcd(); +out: + INTON; + return err; +} + + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. + */ + +STATIC const char * +updatepwd(const char *dir) +{ + char *new; + char *p; + char *cdcomppath; + const char *lim; + + cdcomppath = sstrdup(dir); + STARTSTACKSTR(new); + if (*dir != '/') { + if (curdir == nullstr) + return 0; + new = stputs(curdir, new); + } + new = makestrspace(strlen(dir) + 2, new); + lim = stackblock() + 1; + if (*dir != '/') { + if (new[-1] != '/') + USTPUTC('/', new); + if (new > lim && *lim == '/') + lim++; + } else { + USTPUTC('/', new); + cdcomppath++; + if (dir[1] == '/' && dir[2] != '/') { + USTPUTC('/', new); + cdcomppath++; + lim++; + } + } + p = strtok(cdcomppath, "/"); + while (p) { + switch(*p) { + case '.': + if (p[1] == '.' && p[2] == '\0') { + while (new > lim) { + STUNPUTC(new); + if (new[-1] == '/') + break; + } + break; + } else if (p[1] == '\0') + break; + /* fall through */ + default: + new = stputs(p, new); + USTPUTC('/', new); + } + p = strtok(0, "/"); + } + if (new > lim) + STUNPUTC(new); + *new = 0; + return stackblock(); +} + + +/* + * Find out what the current directory is. If we already know the current + * directory, this routine returns immediately. + */ +inline +STATIC char * +getpwd() +{ +#ifdef __GLIBC__ + char *dir = getcwd(0, 0); + if (dir) + return dir; +#else + char buf[PATH_MAX]; + if(getcwd(buf, sizeof(buf))) + return savestr(buf); +#endif + sh_warnx("getcwd() failed: %s", strerror(errno)); + return nullstr; +} + +int +pwdcmd(int argc, char **argv) +{ + int flags; + const char *dir = curdir; + + flags = cdopt(); + if (flags) { + if (physdir == nullstr) + setpwd(dir, 0); + dir = physdir; + } + out1fmt(snlfmt, dir); + return 0; +} + +void +setpwd(const char *val, int setold) +{ + char *oldcur, *dir; + + oldcur = dir = curdir; + + if (setold) { + setvar("OLDPWD", oldcur, VEXPORT); + } + INTOFF; + if (physdir != nullstr) { + if (physdir != oldcur) + free(physdir); + physdir = nullstr; + } + if (oldcur == val || !val) { + char *s = getpwd(); + physdir = s; + if (!val) + dir = s; + } else + dir = savestr(val); + if (oldcur != dir && oldcur != nullstr) { + free(oldcur); + } + curdir = dir; + INTON; + setvar("PWD", dir, VEXPORT); +} diff --git a/usr/dash/cd.h b/usr/dash/cd.h new file mode 100644 index 0000000..8763161 --- /dev/null +++ b/usr/dash/cd.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. 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. + * 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. + * + */ + +int cdcmd(int, char **); +int pwdcmd(int, char **); +void setpwd(const char *, int); diff --git a/usr/dash/config.h b/usr/dash/config.h new file mode 100644 index 0000000..9757009 --- /dev/null +++ b/usr/dash/config.h @@ -0,0 +1,191 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if __attribute__((__alias__())) is supported */ +#define HAVE_ALIAS_ATTRIBUTE 1 + +/* Define to 1 if you have the <alloca.h> header file. */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the `bsearch' function. */ +#define HAVE_BSEARCH 1 + +/* Define to 1 if you have the declaration of `isblank', and to 0 if you + don't. */ +#define HAVE_DECL_ISBLANK 1 + +/* Define to 1 if you have the `faccessat' function. */ +/* #undef HAVE_FACCESSAT */ + +/* Define to 1 if you have the `fnmatch' function. */ +/* #undef HAVE_FNMATCH */ + +/* Define to 1 if you have the `getpwnam' function. */ +#define HAVE_GETPWNAM 1 + +/* Define to 1 if you have the `getrlimit' function. */ +/* #undef HAVE_GETRLIMIT */ + +/* Define to 1 if you have the `glob' function. */ +/* #undef HAVE_GLOB */ + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `isalpha' function. */ +#define HAVE_ISALPHA 1 + +/* Define to 1 if you have the `killpg' function. */ +/* #undef HAVE_KILLPG */ + +/* Define to 1 if you have the <memory.h> header file. */ +/* #undef HAVE_MEMORY_H */ + +/* Define to 1 if you have the `mempcpy' function. */ +/* #undef HAVE_MEMPCPY */ + +/* Define to 1 if you have the <paths.h> header file. */ +#define HAVE_PATHS_H 1 + +/* Define to 1 if you have the `sigsetmask' function. */ +/* #undef HAVE_SIGSETMASK */ + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +/* #undef HAVE_STPCPY */ + +/* Define to 1 if you have the `strchrnul' function. */ +/* #undef HAVE_STRCHRNUL */ + +/* Define to 1 if you have the <strings.h> header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strsignal' function. */ +#define HAVE_STRSIGNAL 1 + +/* Define to 1 if you have the `strtod' function. */ +/* #undef HAVE_STRTOD */ + +/* Define to 1 if you have the `strtoimax' function. */ +#define HAVE_STRTOIMAX 1 + +/* Define to 1 if you have the `strtoumax' function. */ +#define HAVE_STRTOUMAX 1 + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "dash" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "dash" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "dash 0.5.7" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "dash" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.5.7" + +/* Define to printf format string for intmax_t */ +/* #undef PRIdMAX */ + +/* The size of `intmax_t', as computed by sizeof. */ +#define SIZEOF_INTMAX_T 8 + +/* The size of `long long int', as computed by sizeof. */ +#define SIZEOF_LONG_LONG_INT 8 + +/* Define if you build with -DSMALL */ +#define SMALL 1 + +/* Define to 1 if you have the ANSI C header files. */ +/* #undef STDC_HEADERS */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "0.5.7" + +/* Define if you build with -DWITH_LINENO */ +#define WITH_LINENO 1 + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to system shell path */ +/* #undef _PATH_BSHELL */ + +/* Define to devnull device node path */ +/* #undef _PATH_DEVNULL */ + +/* Define to tty device node path */ +/* #undef _PATH_TTY */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* 64-bit operations are the same as 32-bit */ +#define fstat64 fstat + +/* 64-bit operations are the same as 32-bit */ +#define lstat64 lstat + +/* 64-bit operations are the same as 32-bit */ +#define open64 open + +/* klibc has bsd_signal instead of signal */ +/* #undef signal */ + +/* 64-bit operations are the same as 32-bit */ +#define stat64 stat diff --git a/usr/dash/error.c b/usr/dash/error.c new file mode 100644 index 0000000..9d31989 --- /dev/null +++ b/usr/dash/error.c @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +/* + * Errors and exceptions. + */ + +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +#include "shell.h" +#include "main.h" +#include "options.h" +#include "output.h" +#include "error.h" +#include "show.h" +#include "eval.h" +#include "parser.h" +#include "system.h" + + +/* + * Code to handle exceptions in C. + */ + +struct jmploc *handler; +int exception; +int suppressint; +volatile sig_atomic_t intpending; +int errlinno; + + +static void exverror(int, const char *, va_list) + __attribute__((__noreturn__)); + +/* + * Called to raise an exception. Since C doesn't include exceptions, we + * just do a longjmp to the exception handler. The type of exception is + * stored in the global variable "exception". + */ + +void +exraise(int e) +{ +#ifdef DEBUG + if (handler == NULL) + abort(); +#endif + INTOFF; + + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. (The test for iflag is just + * defensive programming.) + */ + +void +onint(void) { + + intpending = 0; + sigclearmask(); + if (!(rootshell && iflag)) { + signal(SIGINT, SIG_DFL); + raise(SIGINT); + } + exraise(EXINT); + /* NOTREACHED */ +} + +static void +exvwarning2(const char *msg, va_list ap) +{ + struct output *errs; + const char *name; + const char *fmt; + + errs = out2; + name = arg0 ? arg0 : "sh"; + if (!commandname) + fmt = "%s: %d: "; + else + fmt = "%s: %d: %s: "; + outfmt(errs, fmt, name, errlinno, commandname); + doformat(errs, msg, ap); +#if FLUSHERR + outc('\n', errs); +#else + outcslow('\n', errs); +#endif +} + +#define exvwarning(a, b, c) exvwarning2(b, c) + +/* + * Exverror is called to raise the error exception. If the second argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ +static void +exverror(int cond, const char *msg, va_list ap) +{ +#ifdef DEBUG + if (msg) { + va_list aq; + TRACE(("exverror(%d, \"", cond)); + va_copy(aq, ap); + TRACEV((msg, aq)); + va_end(aq); + TRACE(("\") pid=%d\n", getpid())); + } else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); + if (msg) +#endif + exvwarning(-1, msg, ap); + + flushall(); + exraise(cond); + /* NOTREACHED */ +} + + +void +sh_error(const char *msg, ...) +{ + va_list ap; + + exitstatus = 2; + + va_start(ap, msg); + exverror(EXERROR, msg, ap); + /* NOTREACHED */ + va_end(ap); +} + + +void +exerror(int cond, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + exverror(cond, msg, ap); + /* NOTREACHED */ + va_end(ap); +} + +/* + * error/warning routines for external builtins + */ + +void +sh_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + exvwarning(-1, fmt, ap); + va_end(ap); +} + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +const char * +errmsg(int e, int action) +{ + if (e != ENOENT && e != ENOTDIR) + return strerror(e); + + if (action & E_OPEN) + return "No such file"; + else if (action & E_CREAT) + return "Directory nonexistent"; + else + return "not found"; +} + + +#ifdef REALLY_SMALL +void +__inton() { + if (--suppressint == 0 && intpending) { + onint(); + } +} +#endif diff --git a/usr/dash/error.h b/usr/dash/error.h new file mode 100644 index 0000000..f91d11d --- /dev/null +++ b/usr/dash/error.h @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)error.h 8.2 (Berkeley) 5/4/95 + */ + +#ifndef DASH_ERROR_H +#define DASH_ERROR_H + +#include <setjmp.h> +#include <signal.h> + +/* + * Types of operations (passed to the errmsg routine). + */ + +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ + + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exeception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +struct jmploc { + jmp_buf loc; +}; + +extern struct jmploc *handler; +extern int exception; + +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXEXIT 4 /* exit the shell */ + + +/* + * These macros allow the user to suspend the handling of interrupt signals + * over a period of time. This is similar to SIGHOLD to or sigblock, but + * much more efficient and portable. (But hacking the kernel is so much + * more fun than worrying about efficiency and portability. :-)) + */ + +extern int suppressint; +extern volatile sig_atomic_t intpending; + +#define barrier() ({ __asm__ __volatile__ ("": : :"memory"); }) +#define INTOFF \ + ({ \ + suppressint++; \ + barrier(); \ + 0; \ + }) +#ifdef REALLY_SMALL +void __inton(void); +#define INTON __inton() +#else +#define INTON \ + ({ \ + barrier(); \ + if (--suppressint == 0 && intpending) onint(); \ + 0; \ + }) +#endif +#define FORCEINTON \ + ({ \ + barrier(); \ + suppressint = 0; \ + if (intpending) onint(); \ + 0; \ + }) +#define SAVEINT(v) ((v) = suppressint) +#define RESTOREINT(v) \ + ({ \ + barrier(); \ + if ((suppressint = (v)) == 0 && intpending) onint(); \ + 0; \ + }) +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + +void exraise(int) __attribute__((__noreturn__)); +#ifdef USE_NORETURN +void onint(void) __attribute__((__noreturn__)); +#else +void onint(void); +#endif +extern int errlinno; +void sh_error(const char *, ...) __attribute__((__noreturn__)); +void exerror(int, const char *, ...) __attribute__((__noreturn__)); +const char *errmsg(int, int); + +void sh_warnx(const char *, ...); + +#endif /* DASH_ERROR_H */ diff --git a/usr/dash/eval.c b/usr/dash/eval.c new file mode 100644 index 0000000..145e0b4 --- /dev/null +++ b/usr/dash/eval.c @@ -0,0 +1,1092 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <sys/types.h> + +/* + * Evaluate a command. + */ + +#include "shell.h" +#include "nodes.h" +#include "syntax.h" +#include "expand.h" +#include "parser.h" +#include "jobs.h" +#include "eval.h" +#include "builtins.h" +#include "options.h" +#include "exec.h" +#include "redir.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "show.h" +#include "mystring.h" +#ifndef SMALL +#include "myhistedit.h" +#endif + + +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ + +int evalskip; /* set if we are skipping commands */ +STATIC int skipcount; /* number of levels to skip */ +MKINIT int loopnest; /* current loop nesting level */ +static int funcline; /* starting line number of current function, or 0 if not in a function */ + + +char *commandname; +int exitstatus; /* exit status of last command */ +int back_exitstatus; /* exit status of backquoted command */ + + +#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) +STATIC +#endif +void evaltreenr(union node *, int) __attribute__ ((__noreturn__)); +STATIC void evalloop(union node *, int); +STATIC void evalfor(union node *, int); +STATIC void evalcase(union node *, int); +STATIC void evalsubshell(union node *, int); +STATIC void expredir(union node *); +STATIC void evalpipe(union node *, int); +#ifdef notyet +STATIC void evalcommand(union node *, int, struct backcmd *); +#else +STATIC void evalcommand(union node *, int); +#endif +STATIC int evalbltin(const struct builtincmd *, int, char **, int); +STATIC int evalfun(struct funcnode *, int, char **, int); +STATIC void prehash(union node *); +STATIC int eprintlist(struct output *, struct strlist *, int); +STATIC int bltincmd(int, char **); + + +STATIC const struct builtincmd bltin = { + .name = nullstr, + .builtin = bltincmd +}; + + +/* + * Called to reset things after an exception. + */ + +#ifdef mkinit +INCLUDE "eval.h" + +RESET { + evalskip = 0; + loopnest = 0; +} +#endif + + + +/* + * The eval commmand. + */ + +static int evalcmd(int argc, char **argv, int flags) +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + concat = stputs(p, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + return evalstring(p, flags & EV_TESTED); + } + return 0; +} + + +/* + * Execute a command or commands contained in a string. + */ + +int +evalstring(char *s, int flags) +{ + union node *n; + struct stackmark smark; + int status; + + setinputstring(s); + setstackmark(&smark); + + status = 0; + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, flags); + status = exitstatus; + popstackmark(&smark); + if (evalskip) + break; + } + popfile(); + + return status; +} + + + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ + +void +evaltree(union node *n, int flags) +{ + int checkexit = 0; + void (*evalfn)(union node *, int); + unsigned isor; + int status; + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + goto out; + } +#ifndef SMALL + displayhist = 1; /* show history substitutions done with fc */ +#endif + TRACE(("pid %d, evaltree(%p: %d, %d) called\n", + getpid(), n, n->type, flags)); + switch (n->type) { + default: +#ifdef DEBUG + out1fmt("Node type = %d\n", n->type); +#ifndef USE_GLIBC_STDIO + flushout(out1); +#endif + break; +#endif + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + status = !exitstatus; + goto setstatus; + case NREDIR: + errlinno = lineno = n->nredir.linno; + if (funcline) + lineno -= funcline - 1; + expredir(n->nredir.redirect); + pushredir(n->nredir.redirect); + status = redirectsafe(n->nredir.redirect, REDIR_PUSH); + if (!status) { + evaltree(n->nredir.n, flags & EV_TESTED); + status = exitstatus; + } + if (n->nredir.redirect) + popredir(0); + goto setstatus; + case NCMD: +#ifdef notyet + if (eflag && !(flags & EV_TESTED)) + checkexit = ~0; + evalcommand(n, flags, (struct backcmd *)NULL); + break; +#else + evalfn = evalcommand; +checkexit: + if (eflag && !(flags & EV_TESTED)) + checkexit = ~0; + goto calleval; +#endif + case NFOR: + evalfn = evalfor; + goto calleval; + case NWHILE: + case NUNTIL: + evalfn = evalloop; + goto calleval; + case NSUBSHELL: + case NBACKGND: + evalfn = evalsubshell; + goto checkexit; + case NPIPE: + evalfn = evalpipe; + goto checkexit; + case NCASE: + evalfn = evalcase; + goto calleval; + case NAND: + case NOR: + case NSEMI: +#if NAND + 1 != NOR +#error NAND + 1 != NOR +#endif +#if NOR + 1 != NSEMI +#error NOR + 1 != NSEMI +#endif + isor = n->type - NAND; + evaltree( + n->nbinary.ch1, + (flags | ((isor >> 1) - 1)) & EV_TESTED + ); + if ((!exitstatus) == isor) + break; + if (!evalskip) { + n = n->nbinary.ch2; +evaln: + evalfn = evaltree; +calleval: + evalfn(n, flags); + break; + } + break; + case NIF: + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + break; + if (exitstatus == 0) { + n = n->nif.ifpart; + goto evaln; + } else if (n->nif.elsepart) { + n = n->nif.elsepart; + goto evaln; + } + goto success; + case NDEFUN: + defun(n); +success: + status = 0; +setstatus: + exitstatus = status; + break; + } +out: + if (checkexit & exitstatus) + goto exexit; + + if (pendingsigs) + dotrap(); + + if (flags & EV_EXIT) { +exexit: + exraise(EXEXIT); + } +} + + +#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) +STATIC +#endif +void evaltreenr(union node *n, int flags) +#ifdef HAVE_ATTRIBUTE_ALIAS + __attribute__ ((alias("evaltree"))); +#else +{ + evaltree(n, flags); + abort(); +} +#endif + + +STATIC void +evalloop(union node *n, int flags) +{ + int status; + + loopnest++; + status = 0; + flags &= EV_TESTED; + for (;;) { + int i; + + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { +skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + i = exitstatus; + if (n->type != NWHILE) + i = !i; + if (i != 0) + break; + evaltree(n->nbinary.ch2, flags); + status = exitstatus; + if (evalskip) + goto skipping; + } + loopnest--; + exitstatus = status; +} + + + +STATIC void +evalfor(union node *n, int flags) +{ + struct arglist arglist; + union node *argp; + struct strlist *sp; + struct stackmark smark; + + errlinno = lineno = n->nfor.linno; + if (funcline) + lineno -= funcline - 1; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + /* XXX */ + if (evalskip) + goto out; + } + *arglist.lastp = NULL; + + exitstatus = 0; + loopnest++; + flags &= EV_TESTED; + for (sp = arglist.list ; sp ; sp = sp->next) { + setvar(n->nfor.var, sp->text, 0); + evaltree(n->nfor.body, flags); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; +out: + popstackmark(&smark); +} + + + +STATIC void +evalcase(union node *n, int flags) +{ + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + + errlinno = lineno = n->ncase.linno; + if (funcline) + lineno -= funcline - 1; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + expandarg(n->ncase.expr, &arglist, EXP_TILDE); + exitstatus = 0; + for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.list->text)) { + if (evalskip == 0) { + evaltree(cp->nclist.body, flags); + } + goto out; + } + } + } +out: + popstackmark(&smark); +} + + + +/* + * Kick off a subshell to evaluate a tree. + */ + +STATIC void +evalsubshell(union node *n, int flags) +{ + struct job *jp; + int backgnd = (n->type == NBACKGND); + int status; + + errlinno = lineno = n->nredir.linno; + if (funcline) + lineno -= funcline - 1; + + expredir(n->nredir.redirect); + if (!backgnd && flags & EV_EXIT && !have_traps()) + goto nofork; + INTOFF; + jp = makejob(n, 1); + if (forkshell(jp, n, backgnd) == 0) { + INTON; + flags |= EV_EXIT; + if (backgnd) + flags &=~ EV_TESTED; +nofork: + redirect(n->nredir.redirect, 0); + evaltreenr(n->nredir.n, flags); + /* never returns */ + } + status = 0; + if (! backgnd) + status = waitforjob(jp); + exitstatus = status; + INTON; +} + + + +/* + * Compute the names of the files in a redirection list. + */ + +STATIC void +expredir(union node *n) +{ + union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + struct arglist fn; + fn.lastp = &fn.list; + switch (redir->type) { + case NFROMTO: + case NFROM: + case NTO: + case NCLOBBER: + case NAPPEND: + expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); + redir->nfile.expfname = fn.list->text; + break; + case NFROMFD: + case NTOFD: + if (redir->ndup.vname) { + expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); + fixredir(redir, fn.list->text, 1); + } + break; + } + } +} + + + +/* + * Evaluate a pipeline. All the processes in the pipeline are children + * of the process creating the pipeline. (This differs from some versions + * of the shell, which make the last process in a pipeline the parent + * of all the rest.) + */ + +STATIC void +evalpipe(union node *n, int flags) +{ + struct job *jp; + struct nodelist *lp; + int pipelen; + int prevfd; + int pip[2]; + + TRACE(("evalpipe(0x%lx) called\n", (long)n)); + pipelen = 0; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) + pipelen++; + flags |= EV_EXIT; + INTOFF; + jp = makejob(n, pipelen); + prevfd = -1; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + prehash(lp->n); + pip[1] = -1; + if (lp->next) { + if (pipe(pip) < 0) { + close(prevfd); + sh_error("Pipe call failed"); + } + } + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (pip[1] >= 0) { + close(pip[0]); + } + if (prevfd > 0) { + dup2(prevfd, 0); + close(prevfd); + } + if (pip[1] > 1) { + dup2(pip[1], 1); + close(pip[1]); + } + evaltreenr(lp->n, flags); + /* never returns */ + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + if (n->npipe.backgnd == 0) { + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + } + INTON; +} + + + +/* + * Execute a command inside back quotes. If it's a builtin command, we + * want to save its output in a block obtained from malloc. Otherwise + * we fork off a subprocess and get the output of the command via a pipe. + * Should be called with interrupts off. + */ + +void +evalbackcmd(union node *n, struct backcmd *result) +{ + int pip[2]; + struct job *jp; + + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + goto out; + } + + if (pipe(pip) < 0) + sh_error("Pipe call failed"); + jp = makejob(n, 1); + if (forkshell(jp, n, FORK_NOJOB) == 0) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + dup2(pip[1], 1); + close(pip[1]); + } + ifsfree(); + evaltreenr(n, EV_EXIT); + /* NOTREACHED */ + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; + +out: + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", + result->fd, result->buf, result->nleft, result->jp)); +} + +static char ** +parse_command_args(char **argv, const char **path) +{ + char *cp, c; + + for (;;) { + cp = *++argv; + if (!cp) + return 0; + if (*cp++ != '-') + break; + if (!(c = *cp++)) + break; + if (c == '-' && !*cp) { + if (!*++argv) + return 0; + break; + } + do { + switch (c) { + case 'p': + *path = defpath; + break; + default: + /* run 'typecmd' for other options */ + return 0; + } + } while ((c = *cp++)); + } + return argv; +} + + + +/* + * Execute a simple command. + */ + +STATIC void +#ifdef notyet +evalcommand(union node *cmd, int flags, struct backcmd *backcmd) +#else +evalcommand(union node *cmd, int flags) +#endif +{ + struct localvar_list *localvar_stop; + struct redirtab *redir_stop; + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + struct strlist *sp; +#ifdef notyet + int pip[2]; +#endif + struct cmdentry cmdentry; + struct job *jp; + char *lastarg; + const char *path; + int spclbltin; + int execcmd; + int status; + char **nargv; + + errlinno = lineno = cmd->ncmd.linno; + if (funcline) + lineno -= funcline - 1; + + /* First expand the arguments. */ + TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); + setstackmark(&smark); + localvar_stop = pushlocalvars(); + back_exitstatus = 0; + + cmdentry.cmdtype = CMDBUILTIN; + cmdentry.u.cmd = &bltin; + varlist.lastp = &varlist.list; + *varlist.lastp = NULL; + arglist.lastp = &arglist.list; + *arglist.lastp = NULL; + + argc = 0; + for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) { + struct strlist **spp; + + spp = arglist.lastp; + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + for (sp = *spp; sp; sp = sp->next) + argc++; + } + + /* Reserve one extra spot at the front for shellexec. */ + nargv = stalloc(sizeof (char *) * (argc + 2)); + argv = ++nargv; + for (sp = arglist.list ; sp ; sp = sp->next) { + TRACE(("evalcommand arg: %s\n", sp->text)); + *nargv++ = sp->text; + } + *nargv = NULL; + + lastarg = NULL; + if (iflag && funcline == 0 && argc > 0) + lastarg = nargv[-1]; + + preverrout.fd = 2; + expredir(cmd->ncmd.redirect); + redir_stop = pushredir(cmd->ncmd.redirect); + status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2); + + path = vpath.text; + for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { + struct strlist **spp; + char *p; + + spp = varlist.lastp; + expandarg(argp, &varlist, EXP_VARTILDE); + + mklocal((*spp)->text); + + /* + * Modify the command lookup path, if a PATH= assignment + * is present + */ + p = (*spp)->text; + if (varequal(p, path)) + path = p; + } + + /* Print the command if xflag is set. */ + if (xflag) { + struct output *out; + int sep; + + out = &preverrout; + outstr(expandstr(ps4val()), out); + sep = 0; + sep = eprintlist(out, varlist.list, sep); + eprintlist(out, arglist.list, sep); + outcslow('\n', out); +#ifdef FLUSHERR + flushout(out); +#endif + } + + execcmd = 0; + spclbltin = -1; + + /* Now locate the command. */ + if (argc) { + const char *oldpath; + int cmd_flag = DO_ERR; + + path += 5; + oldpath = path; + for (;;) { + find_command(argv[0], &cmdentry, cmd_flag, path); + if (cmdentry.cmdtype == CMDUNKNOWN) { + status = 127; +#ifdef FLUSHERR + flushout(&errout); +#endif + goto bail; + } + + /* implement bltin and command here */ + if (cmdentry.cmdtype != CMDBUILTIN) + break; + if (spclbltin < 0) + spclbltin = + cmdentry.u.cmd->flags & + BUILTIN_SPECIAL + ; + if (cmdentry.u.cmd == EXECCMD) + execcmd++; + if (cmdentry.u.cmd != COMMANDCMD) + break; + + path = oldpath; + nargv = parse_command_args(argv, &path); + if (!nargv) + break; + argc -= nargv - argv; + argv = nargv; + cmd_flag |= DO_NOFUNC; + } + } + + if (status) { +bail: + exitstatus = status; + + /* We have a redirection error. */ + if (spclbltin > 0) + exraise(EXERROR); + + goto out; + } + + /* Execute the command. */ + switch (cmdentry.cmdtype) { + default: + /* Fork off a child process if necessary. */ + if (!(flags & EV_EXIT) || have_traps()) { + INTOFF; + jp = makejob(cmd, 1); + if (forkshell(jp, cmd, FORK_FG) != 0) { + exitstatus = waitforjob(jp); + INTON; + break; + } + FORCEINTON; + } + listsetvar(varlist.list, VEXPORT|VSTACK); + shellexec(argv, path, cmdentry.u.index); + /* NOTREACHED */ + + case CMDBUILTIN: + if (spclbltin > 0 || argc == 0) { + poplocalvars(1); + if (execcmd && argc > 1) + listsetvar(varlist.list, VEXPORT); + } + if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { + int status; + int i; + + i = exception; + if (i == EXEXIT) + goto raise; + + status = (i == EXINT) ? SIGINT + 128 : 2; + exitstatus = status; + + if (i == EXINT || spclbltin > 0) { +raise: + longjmp(handler->loc, 1); + } + FORCEINTON; + } + break; + + case CMDFUNCTION: + poplocalvars(1); + if (evalfun(cmdentry.u.func, argc, argv, flags)) + goto raise; + break; + } + +out: + if (cmd->ncmd.redirect) + popredir(execcmd); + unwindredir(redir_stop); + unwindlocalvars(localvar_stop); + if (lastarg) + /* dsl: I think this is intended to be used to support + * '_' in 'vi' command mode during line editing... + * However I implemented that within libedit itself. + */ + setvar("_", lastarg, 0); + popstackmark(&smark); +} + +STATIC int +evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags) +{ + char *volatile savecmdname; + struct jmploc *volatile savehandler; + struct jmploc jmploc; + int status; + int i; + + savecmdname = commandname; + savehandler = handler; + if ((i = setjmp(jmploc.loc))) + goto cmddone; + handler = &jmploc; + commandname = argv[0]; + argptr = argv + 1; + optptr = NULL; /* initialize nextopt */ + if (cmd == EVALCMD) + status = evalcmd(argc, argv, flags); + else + status = (*cmd->builtin)(argc, argv); + flushall(); + status |= outerr(out1); + exitstatus = status; +cmddone: + freestdout(); + commandname = savecmdname; + handler = savehandler; + + return i; +} + +STATIC int +evalfun(struct funcnode *func, int argc, char **argv, int flags) +{ + volatile struct shparam saveparam; + struct jmploc *volatile savehandler; + struct jmploc jmploc; + int e; + int savefuncline; + + saveparam = shellparam; + savefuncline = funcline; + savehandler = handler; + if ((e = setjmp(jmploc.loc))) { + goto funcdone; + } + INTOFF; + handler = &jmploc; + shellparam.malloc = 0; + func->count++; + funcline = func->n.ndefun.linno; + INTON; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + shellparam.optind = 1; + shellparam.optoff = -1; + pushlocalvars(); + evaltree(func->n.ndefun.body, flags & EV_TESTED); + poplocalvars(0); +funcdone: + INTOFF; + funcline = savefuncline; + freefunc(func); + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + INTON; + evalskip &= ~SKIPFUNC; + return e; +} + + +/* + * Search for a command. This is called before we fork so that the + * location of the command will be available in the parent as well as + * the child. The check for "goodname" is an overly conservative + * check that the name will not be subject to expansion. + */ + +STATIC void +prehash(union node *n) +{ + struct cmdentry entry; + + if (n->type == NCMD && n->ncmd.args) + if (goodname(n->ncmd.args->narg.text)) + find_command(n->ncmd.args->narg.text, &entry, 0, + pathval()); +} + + + +/* + * Builtin commands. Builtin commands whose functions are closely + * tied to evaluation are implemented here. + */ + +/* + * No command given. + */ + +STATIC int +bltincmd(int argc, char **argv) +{ + /* + * Preserve exitstatus of a previous possible redirection + * as POSIX mandates + */ + return back_exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +int +breakcmd(int argc, char **argv) +{ + int n = argc > 1 ? number(argv[1]) : 1; + + if (n <= 0) + badnum(argv[1]); + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +int +returncmd(int argc, char **argv) +{ + /* + * If called outside a function, do what ksh does; + * skip the rest of the file. + */ + evalskip = SKIPFUNC; + return argv[1] ? number(argv[1]) : exitstatus; +} + + +int +falsecmd(int argc, char **argv) +{ + return 1; +} + + +int +truecmd(int argc, char **argv) +{ + return 0; +} + + +int +execcmd(int argc, char **argv) +{ + if (argc > 1) { + iflag = 0; /* exit on error */ + mflag = 0; + optschanged(); + shellexec(argv + 1, pathval(), 0); + } + return 0; +} + + +STATIC int +eprintlist(struct output *out, struct strlist *sp, int sep) +{ + while (sp) { + const char *p; + + p = " %s"; + p += (1 - sep); + sep |= 1; + outfmt(out, p, sp->text); + sp = sp->next; + } + + return sep; +} diff --git a/usr/dash/eval.h b/usr/dash/eval.h new file mode 100644 index 0000000..5ccfa9f --- /dev/null +++ b/usr/dash/eval.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)eval.h 8.2 (Berkeley) 5/4/95 + */ + +extern char *commandname; /* currently executing command */ +extern int exitstatus; /* exit status of last command */ +extern int back_exitstatus; /* exit status of backquoted command */ + + +struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ + char *buf; /* buffer */ + int nleft; /* number of chars in buffer */ + struct job *jp; /* job structure for command */ +}; + +int evalstring(char *, int); +union node; /* BLETCH for ansi C */ +void evaltree(union node *, int); +void evalbackcmd(union node *, struct backcmd *); + +extern int evalskip; + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK (1 << 0) +#define SKIPCONT (1 << 1) +#define SKIPFUNC (1 << 2) diff --git a/usr/dash/exec.c b/usr/dash/exec.c new file mode 100644 index 0000000..79e2007 --- /dev/null +++ b/usr/dash/exec.c @@ -0,0 +1,859 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +/* + * When commands are first encountered, they are entered in a hash table. + * This ensures that a full path search will not have to be done for them + * on each invocation. + * + * We should investigate converting to a linear search, even though that + * would make the command name "hash" a misnomer. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "parser.h" +#include "redir.h" +#include "eval.h" +#include "exec.h" +#include "builtins.h" +#include "var.h" +#include "options.h" +#include "output.h" +#include "syntax.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" +#include "show.h" +#include "jobs.h" +#include "alias.h" +#include "system.h" + + +#define CMDTABLESIZE 31 /* should be prime */ +#define ARB 1 /* actual size determined at run time */ + + + +struct tblentry { + struct tblentry *next; /* next entry in hash chain */ + union param param; /* definition of builtin function */ + short cmdtype; /* index identifying command */ + char rehash; /* if set, cd done since entry created */ + char cmdname[ARB]; /* name of command */ +}; + + +STATIC struct tblentry *cmdtable[CMDTABLESIZE]; +STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ + + +STATIC void tryexec(char *, char **, char **); +STATIC void printentry(struct tblentry *); +STATIC void clearcmdentry(int); +STATIC struct tblentry *cmdlookup(const char *, int); +STATIC void delete_cmd_entry(void); +STATIC void addcmdentry(char *, struct cmdentry *); +STATIC int describe_command(struct output *, char *, int); + + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + */ + +void +shellexec(char **argv, const char *path, int idx) +{ + char *cmdname; + int e; + char **envp; + int exerrno; + + envp = environment(); + if (strchr(argv[0], '/') != NULL) { + tryexec(argv[0], argv, envp); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--idx < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp); + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + } + stunalloc(cmdname); + } + } + + /* Map to POSIX errors */ + switch (e) { + case EACCES: + exerrno = 126; + break; + case ENOENT: + exerrno = 127; + break; + default: + exerrno = 2; + break; + } + exitstatus = exerrno; + TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", + argv[0], e, suppressint )); + exerror(EXEXIT, "%s: %s", argv[0], errmsg(e, E_EXEC)); + /* NOTREACHED */ +} + + +STATIC void +tryexec(char *cmd, char **argv, char **envp) +{ + char *const path_bshell = _PATH_BSHELL; + +repeat: +#ifdef SYSV + do { + execve(cmd, argv, envp); + } while (errno == EINTR); +#else + execve(cmd, argv, envp); +#endif + if (cmd != path_bshell && errno == ENOEXEC) { + *argv-- = cmd; + *argv = cmd = path_bshell; + goto repeat; + } +} + + + +/* + * Do a path search. The variable path (passed by reference) should be + * set to the start of the path before the first call; padvance will update + * this value as it proceeds. Successive calls to padvance will return + * the possible path expansions in sequence. If an option (indicated by + * a percent sign) appears in the path entry then the global variable + * pathopt will be set to point to it; otherwise pathopt will be set to + * NULL. + */ + +const char *pathopt; + +char * +padvance(const char **path, const char *name) +{ + const char *p; + char *q; + const char *start; + size_t len; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start ; *p && *p != ':' && *p != '%' ; p++); + len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ + while (stackblocksize() < len) + growstackblock(); + q = stackblock(); + if (p != start) { + memcpy(q, start, p - start); + q += p - start; + *q++ = '/'; + } + strcpy(q, name); + pathopt = NULL; + if (*p == '%') { + pathopt = ++p; + while (*p && *p != ':') p++; + } + if (*p == ':') + *path = p + 1; + else + *path = NULL; + return stalloc(len); +} + + + +/*** Command hashing code ***/ + + +int +hashcmd(int argc, char **argv) +{ + struct tblentry **pp; + struct tblentry *cmdp; + int c; + struct cmdentry entry; + char *name; + + while ((c = nextopt("r")) != '\0') { + clearcmdentry(0); + return 0; + } + if (*argptr == NULL) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL) + printentry(cmdp); + } + } + return 0; + } + c = 0; + while ((name = *argptr) != NULL) { + if ((cmdp = cmdlookup(name, 0)) != NULL + && (cmdp->cmdtype == CMDNORMAL + || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) + delete_cmd_entry(); + find_command(name, &entry, DO_ERR, pathval()); + if (entry.cmdtype == CMDUNKNOWN) + c = 1; + argptr++; + } + return c; +} + + +STATIC void +printentry(struct tblentry *cmdp) +{ + int idx; + const char *path; + char *name; + + idx = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--idx >= 0); + out1str(name); + out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr); +} + + + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +void +find_command(char *name, struct cmdentry *entry, int act, const char *path) +{ + struct tblentry *cmdp; + int idx; + int prev; + char *fullname; + struct stat64 statb; + int e; + int updatetbl; + struct builtincmd *bcmd; + + /* If name contains a slash, don't use PATH or hash table */ + if (strchr(name, '/') != NULL) { + entry->u.index = -1; + if (act & DO_ABS) { + while (stat64(name, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + entry->cmdtype = CMDUNKNOWN; + return; + } + } + entry->cmdtype = CMDNORMAL; + return; + } + + updatetbl = (path == pathval()); + if (!updatetbl) { + act |= DO_ALTPATH; + if (strstr(path, "%builtin") != NULL) + act |= DO_ALTBLTIN; + } + + /* If name is in the table, check answer will be ok */ + if ((cmdp = cmdlookup(name, 0)) != NULL) { + int bit; + + switch (cmdp->cmdtype) { + default: +#if DEBUG + abort(); +#endif + case CMDNORMAL: + bit = DO_ALTPATH; + break; + case CMDFUNCTION: + bit = DO_NOFUNC; + break; + case CMDBUILTIN: + bit = DO_ALTBLTIN; + break; + } + if (act & bit) { + updatetbl = 0; + cmdp = NULL; + } else if (cmdp->rehash == 0) + /* if not invalidated by cd, we're done */ + goto success; + } + + /* If %builtin not in path, check for builtin next */ + bcmd = find_builtin(name); + if (bcmd && (bcmd->flags & BUILTIN_REGULAR || ( + act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0 + ))) + goto builtin_success; + + /* We have to search path. */ + prev = -1; /* where to start */ + if (cmdp && cmdp->rehash) { /* doing a rehash */ + if (cmdp->cmdtype == CMDBUILTIN) + prev = builtinloc; + else + prev = cmdp->param.index; + } + + e = ENOENT; + idx = -1; +loop: + while ((fullname = padvance(&path, name)) != NULL) { + stunalloc(fullname); + idx++; + if (pathopt) { + if (prefix(pathopt, "builtin")) { + if (bcmd) + goto builtin_success; + continue; + } else if (!(act & DO_NOFUNC) && + prefix(pathopt, "func")) { + /* handled below */ + } else { + /* ignore unimplemented options */ + continue; + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && idx <= prev) { + if (idx < prev) + continue; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat64(fullname, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + goto loop; + } + e = EACCES; /* if we fail, this will be the error */ + if (!S_ISREG(statb.st_mode)) + continue; + if (pathopt) { /* this is a %func directory */ + stalloc(strlen(fullname) + 1); + readcmdfile(fullname); + if ((cmdp = cmdlookup(name, 0)) == NULL || + cmdp->cmdtype != CMDFUNCTION) + sh_error("%s not defined in %s", name, + fullname); + stunalloc(fullname); + goto success; + } +#ifdef notdef + /* XXX this code stops root executing stuff, and is buggy + if you need a group from the group list. */ + if (statb.st_uid == geteuid()) { + if ((statb.st_mode & 0100) == 0) + goto loop; + } else if (statb.st_gid == getegid()) { + if ((statb.st_mode & 010) == 0) + goto loop; + } else { + if ((statb.st_mode & 01) == 0) + goto loop; + } +#endif + TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); + if (!updatetbl) { + entry->cmdtype = CMDNORMAL; + entry->u.index = idx; + return; + } + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDNORMAL; + cmdp->param.index = idx; + INTON; + goto success; + } + + /* We failed. If there was an entry for this command, delete it */ + if (cmdp && updatetbl) + delete_cmd_entry(); + if (act & DO_ERR) + sh_warnx("%s: %s", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +builtin_success: + if (!updatetbl) { + entry->cmdtype = CMDBUILTIN; + entry->u.cmd = bcmd; + return; + } + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.cmd = bcmd; + INTON; +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; +} + + + +/* + * Search the table of builtin commands. + */ + +struct builtincmd * +find_builtin(const char *name) +{ + struct builtincmd *bp; + + bp = bsearch( + &name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd), + pstrcmp + ); + return bp; +} + + + +/* + * Called when a cd is done. Marks all commands so the next time they + * are executed they will be rehashed. + */ + +void +hashcd(void) +{ + struct tblentry **pp; + struct tblentry *cmdp; + + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL || ( + cmdp->cmdtype == CMDBUILTIN && + !(cmdp->param.cmd->flags & BUILTIN_REGULAR) && + builtinloc > 0 + )) + cmdp->rehash = 1; + } + } +} + + + +/* + * Fix command hash table when PATH changed. + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. + * Called with interrupts off. + */ + +void +changepath(const char *newval) +{ + const char *old, *new; + int idx; + int firstchange; + int bltin; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + idx = 0; + bltin = -1; + for (;;) { + if (*old != *new) { + firstchange = idx; + if ((*old == '\0' && *new == ':') + || (*old == ':' && *new == '\0')) + firstchange++; + old = new; /* ignore subsequent differences */ + } + if (*new == '\0') + break; + if (*new == '%' && bltin < 0 && prefix(new + 1, "builtin")) + bltin = idx; + if (*new == ':') { + idx++; + } + new++, old++; + } + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + if (builtinloc >= 0 && bltin < 0) + firstchange = 0; + clearcmdentry(firstchange); + builtinloc = bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +STATIC void +clearcmdentry(int firstchange) +{ + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if ((cmdp->cmdtype == CMDNORMAL && + cmdp->param.index >= firstchange) + || (cmdp->cmdtype == CMDBUILTIN && + builtinloc >= firstchange)) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + * + * Interrupts must be off if called with add != 0. + */ + +struct tblentry **lastcmdentry; + + +STATIC struct tblentry * +cmdlookup(const char *name, int add) +{ + unsigned int hashval; + const char *p; + struct tblentry *cmdp; + struct tblentry **pp; + + p = name; + hashval = (unsigned char)*p << 4; + while (*p) + hashval += (unsigned char)*p++; + hashval &= 0x7FFF; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB + + strlen(name) + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + strcpy(cmdp->cmdname, name); + } + lastcmdentry = pp; + return cmdp; +} + +/* + * Delete the command entry returned on the last lookup. + */ + +STATIC void +delete_cmd_entry(void) +{ + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + if (cmdp->cmdtype == CMDFUNCTION) + freefunc(cmdp->param.func); + ckfree(cmdp); + INTON; +} + + + +#ifdef notdef +void +getcmdentry(char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp = cmdlookup(name, 0); + + if (cmdp) { + entry->u = cmdp->param; + entry->cmdtype = cmdp->cmdtype; + } else { + entry->cmdtype = CMDUNKNOWN; + entry->u.index = 0; + } +} +#endif + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name - except special builtins. + */ + +STATIC void +addcmdentry(char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp; + + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + cmdp->rehash = 0; +} + + +/* + * Define a shell function. + */ + +void +defun(union node *func) +{ + struct cmdentry entry; + + INTOFF; + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(func); + addcmdentry(func->ndefun.text, &entry); + INTON; +} + + +/* + * Delete a function if it exists. + */ + +void +unsetfunc(const char *name) +{ + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && + cmdp->cmdtype == CMDFUNCTION) + delete_cmd_entry(); +} + +/* + * Locate and print what a word is... + */ + +int +typecmd(int argc, char **argv) +{ + int i; + int err = 0; + + for (i = 1; i < argc; i++) { + err |= describe_command(out1, argv[i], 1); + } + return err; +} + +STATIC int +describe_command(out, command, verbose) + struct output *out; + char *command; + int verbose; +{ + struct cmdentry entry; + struct tblentry *cmdp; + const struct alias *ap; + const char *path = pathval(); + + if (verbose) { + outstr(command, out); + } + + /* First look at the keywords */ + if (findkwd(command)) { + outstr(verbose ? " is a shell keyword" : command, out); + goto out; + } + + /* Then look at the aliases */ + if ((ap = lookupalias(command, 0)) != NULL) { + if (verbose) { + outfmt(out, " is an alias for %s", ap->val); + } else { + outstr("alias ", out); + printalias(ap); + return 0; + } + goto out; + } + + /* Then check if it is a tracked alias */ + if ((cmdp = cmdlookup(command, 0)) != NULL) { + entry.cmdtype = cmdp->cmdtype; + entry.u = cmdp->param; + } else { + /* Finally use brute force */ + find_command(command, &entry, DO_ABS, path); + } + + switch (entry.cmdtype) { + case CMDNORMAL: { + int j = entry.u.index; + char *p; + if (j == -1) { + p = command; + } else { + do { + p = padvance(&path, command); + stunalloc(p); + } while (--j >= 0); + } + if (verbose) { + outfmt( + out, " is%s %s", + cmdp ? " a tracked alias for" : nullstr, p + ); + } else { + outstr(p, out); + } + break; + } + + case CMDFUNCTION: + if (verbose) { + outstr(" is a shell function", out); + } else { + outstr(command, out); + } + break; + + case CMDBUILTIN: + if (verbose) { + outfmt( + out, " is a %sshell builtin", + entry.u.cmd->flags & BUILTIN_SPECIAL ? + "special " : nullstr + ); + } else { + outstr(command, out); + } + break; + + default: + if (verbose) { + outstr(": not found\n", out); + } + return 127; + } + +out: + outc('\n', out); + return 0; +} + +int +commandcmd(argc, argv) + int argc; + char **argv; +{ + char *cmd; + int c; + enum { + VERIFY_BRIEF = 1, + VERIFY_VERBOSE = 2, + } verify = 0; + + while ((c = nextopt("pvV")) != '\0') + if (c == 'V') + verify |= VERIFY_VERBOSE; + else if (c == 'v') + verify |= VERIFY_BRIEF; +#ifdef DEBUG + else if (c != 'p') + abort(); +#endif + + cmd = *argptr; + if (verify && cmd) + return describe_command(out1, cmd, verify - VERIFY_BRIEF); + + return 0; +} diff --git a/usr/dash/exec.h b/usr/dash/exec.h new file mode 100644 index 0000000..9ccb305 --- /dev/null +++ b/usr/dash/exec.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)exec.h 8.3 (Berkeley) 6/8/95 + */ + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDFUNCTION 1 /* command is a shell function */ +#define CMDBUILTIN 2 /* command is a shell builtin */ + + +struct cmdentry { + int cmdtype; + union param { + int index; + const struct builtincmd *cmd; + struct funcnode *func; + } u; +}; + + +/* action to find_command() */ +#define DO_ERR 0x01 /* prints errors */ +#define DO_ABS 0x02 /* checks absolute paths */ +#define DO_NOFUNC 0x04 /* don't return shell functions, for command */ +#define DO_ALTPATH 0x08 /* using alternate path */ +#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ + +extern const char *pathopt; /* set by padvance */ + +void shellexec(char **, const char *, int) + __attribute__((__noreturn__)); +char *padvance(const char **, const char *); +int hashcmd(int, char **); +void find_command(char *, struct cmdentry *, int, const char *); +struct builtincmd *find_builtin(const char *); +void hashcd(void); +void changepath(const char *); +#ifdef notdef +void getcmdentry(char *, struct cmdentry *); +#endif +void defun(union node *); +void unsetfunc(const char *); +int typecmd(int, char **); +int commandcmd(int, char **); diff --git a/usr/dash/expand.c b/usr/dash/expand.c new file mode 100644 index 0000000..355e924 --- /dev/null +++ b/usr/dash/expand.c @@ -0,0 +1,1728 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#ifdef HAVE_GETPWNAM +#include <pwd.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <limits.h> +#include <string.h> +#include <fnmatch.h> +#ifdef HAVE_GLOB +#include <glob.h> +#endif +#include <ctype.h> + +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "eval.h" +#include "expand.h" +#include "syntax.h" +#include "parser.h" +#include "jobs.h" +#include "options.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "show.h" +#include "system.h" + +/* + * _rmescape() flags + */ +#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ +#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ +#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ +#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ + +/* Add CTLESC when necessary. */ +#define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT) +/* Do not skip NUL characters. */ +#define QUOTES_KEEPNUL EXP_TILDE + +/* + * Structure specifying which parts of the string should be searched + * for IFS characters. + */ + +struct ifsregion { + struct ifsregion *next; /* next region in list */ + int begoff; /* offset of start of region */ + int endoff; /* offset of end of region */ + int nulonly; /* search for nul bytes only */ +}; + +/* output of current string */ +static char *expdest; +/* list of back quote expressions */ +static struct nodelist *argbackq; +/* first struct in list of ifs regions */ +static struct ifsregion ifsfirst; +/* last struct in list */ +static struct ifsregion *ifslastp; +/* holds expanded arg list */ +static struct arglist exparg; + +STATIC void argstr(char *, int); +STATIC char *exptilde(char *, char *, int); +STATIC void expbackq(union node *, int); +STATIC const char *subevalvar(char *, char *, int, int, int, int, int); +STATIC char *evalvar(char *, int); +STATIC size_t strtodest(const char *, const char *, int); +STATIC void memtodest(const char *, size_t, const char *, int); +STATIC ssize_t varvalue(char *, int, int); +STATIC void expandmeta(struct strlist *, int); +#ifdef HAVE_GLOB +STATIC void addglob(const glob_t *); +#else +STATIC void expmeta(char *, char *); +STATIC struct strlist *expsort(struct strlist *); +STATIC struct strlist *msort(struct strlist *, int); +#endif +STATIC void addfname(char *); +STATIC int patmatch(char *, const char *); +#ifndef HAVE_FNMATCH +STATIC int pmatch(const char *, const char *); +#else +#define pmatch(a, b) !fnmatch((a), (b), 0) +#endif +STATIC int cvtnum(intmax_t); +STATIC size_t esclen(const char *, const char *); +STATIC char *scanleft(char *, char *, char *, char *, int, int); +STATIC char *scanright(char *, char *, char *, char *, int, int); +STATIC void varunset(const char *, const char *, const char *, int) + __attribute__((__noreturn__)); + + +/* + * Prepare a pattern for a glob(3) call. + * + * Returns an stalloced string. + */ + +STATIC inline char * +preglob(const char *pattern, int flag) { + flag |= RMESCAPE_GLOB; + return _rmescapes((char *)pattern, flag); +} + + +STATIC size_t +esclen(const char *start, const char *p) { + size_t esc = 0; + + while (p > start && *--p == (char)CTLESC) { + esc++; + } + return esc; +} + + +static inline const char *getpwhome(const char *name) +{ +#ifdef HAVE_GETPWNAM + struct passwd *pw = getpwnam(name); + return pw ? pw->pw_dir : 0; +#else + return 0; +#endif +} + + +/* + * Perform variable substitution and command substitution on an argument, + * placing the resulting list of arguments in arglist. If EXP_FULL is true, + * perform splitting and file name expansion. When arglist is NULL, perform + * here document expansion. + */ + +void +expandarg(union node *arg, struct arglist *arglist, int flag) +{ + struct strlist *sp; + char *p; + + argbackq = arg->narg.backquote; + STARTSTACKSTR(expdest); + argstr(arg->narg.text, flag); + p = _STPUTC('\0', expdest); + expdest = p - 1; + if (arglist == NULL) { + /* here document expanded */ + goto out; + } + p = grabstackstr(p); + exparg.lastp = &exparg.list; + /* + * TODO - EXP_REDIR + */ + if (flag & EXP_FULL) { + ifsbreakup(p, &exparg); + *exparg.lastp = NULL; + exparg.lastp = &exparg.list; + expandmeta(exparg.list, flag); + } else { + sp = (struct strlist *)stalloc(sizeof (struct strlist)); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; + } + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } + +out: + ifsfree(); +} + + + +/* + * Perform variable and command substitution. If EXP_FULL is set, output CTLESC + * characters to allow for further processing. Otherwise treat + * $@ like $* since no splitting will be performed. + */ + +STATIC void +argstr(char *p, int flag) +{ + static const char spclchars[] = { + '=', + ':', + CTLQUOTEMARK, + CTLENDVAR, + CTLESC, + CTLVAR, + CTLBACKQ, + CTLENDARI, + 0 + }; + const char *reject = spclchars; + int c; + int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD; + int inquotes; + size_t length; + int startloc; + + if (!(flag & EXP_VARTILDE)) { + reject += 2; + } else if (flag & EXP_VARTILDE2) { + reject++; + } + inquotes = 0; + length = 0; + if (flag & EXP_TILDE) { + char *q; + + flag &= ~EXP_TILDE; +tilde: + q = p; + if (*q == '~') + p = exptilde(p, q, flag); + } +start: + startloc = expdest - (char *)stackblock(); + for (;;) { + length += strcspn(p + length, reject); + c = (signed char)p[length]; + if (c && (!(c & 0x80) || c == CTLENDARI)) { + /* c == '=' || c == ':' || c == CTLENDARI */ + length++; + } + if (length > 0) { + int newloc; + expdest = stnputs(p, length, expdest); + newloc = expdest - (char *)stackblock(); + if (breakall && !inquotes && newloc > startloc) { + recordregion(startloc, newloc, 0); + } + startloc = newloc; + } + p += length + 1; + length = 0; + + switch (c) { + case '\0': + goto breakloop; + case '=': + if (flag & EXP_VARTILDE2) { + p--; + continue; + } + flag |= EXP_VARTILDE2; + reject++; + /* fall through */ + case ':': + /* + * sort of a hack - expand tildes in variable + * assignments (after the first '=' and after ':'s). + */ + if (*--p == '~') { + goto tilde; + } + continue; + } + + switch (c) { + case CTLENDVAR: /* ??? */ + goto breakloop; + case CTLQUOTEMARK: + inquotes ^= EXP_QUOTED; + /* "$@" syntax adherence hack */ + if (inquotes && !memcmp(p, dolatstr + 1, + DOLATSTRLEN - 1)) { + p = evalvar(p + 1, flag | inquotes) + 1; + goto start; + } +addquote: + if (flag & QUOTES_ESC) { + p--; + length++; + startloc++; + } + break; + case CTLESC: + startloc++; + length++; + + /* + * Quoted parameter expansion pattern: remove quote + * unless inside inner quotes or we have a literal + * backslash. + */ + if (((flag | inquotes) & (EXP_QPAT | EXP_QUOTED)) == + EXP_QPAT && *p != '\\') + break; + + goto addquote; + case CTLVAR: + p = evalvar(p, flag | inquotes); + goto start; + case CTLBACKQ: + expbackq(argbackq->n, flag | inquotes); + argbackq = argbackq->next; + goto start; + case CTLENDARI: + p--; + expari(flag | inquotes); + goto start; + } + } +breakloop: + ; +} + +STATIC char * +exptilde(char *startp, char *p, int flag) +{ + signed char c; + char *name; + const char *home; + int quotes = flag & QUOTES_ESC; + + name = p + 1; + + while ((c = *++p) != '\0') { + switch(c) { + case CTLESC: + return (startp); + case CTLQUOTEMARK: + return (startp); + case ':': + if (flag & EXP_VARTILDE) + goto done; + break; + case '/': + case CTLENDVAR: + goto done; + } + } +done: + *p = '\0'; + if (*name == '\0') { + home = lookupvar(homestr); + } else { + home = getpwhome(name); + } + if (!home || !*home) + goto lose; + *p = c; + strtodest(home, SQSYNTAX, quotes); + return (p); +lose: + *p = c; + return (startp); +} + + +void +removerecordregions(int endoff) +{ + if (ifslastp == NULL) + return; + + if (ifsfirst.endoff > endoff) { + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + if (ifsfirst.begoff > endoff) + ifslastp = NULL; + else { + ifslastp = &ifsfirst; + ifsfirst.endoff = endoff; + } + return; + } + + ifslastp = &ifsfirst; + while (ifslastp->next && ifslastp->next->begoff < endoff) + ifslastp=ifslastp->next; + while (ifslastp->next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifslastp->next->next; + ckfree(ifslastp->next); + ifslastp->next = ifsp; + INTON; + } + if (ifslastp->endoff > endoff) + ifslastp->endoff = endoff; +} + + +/* + * Expand arithmetic expression. Backup to start of expression, + * evaluate, place result in (backed up) result, adjust string position. + */ +void +expari(int flag) +{ + struct stackmark sm; + char *p, *start; + int begoff; + int len; + intmax_t result; + + /* ifsfree(); */ + + /* + * This routine is slightly over-complicated for + * efficiency. Next we scan backwards looking for the + * start of arithmetic. + */ + start = stackblock(); + p = expdest; + pushstackmark(&sm, p - start); + *--p = '\0'; + p--; + do { + int esc; + + while (*p != (char)CTLARI) { + p--; +#ifdef DEBUG + if (p < start) { + sh_error("missing CTLARI (shouldn't happen)"); + } +#endif + } + + esc = esclen(start, p); + if (!(esc % 2)) { + break; + } + + p -= esc + 1; + } while (1); + + begoff = p - start; + + removerecordregions(begoff); + + expdest = p; + + if (likely(flag & QUOTES_ESC)) + rmescapes(p + 1); + + result = arith(p + 1); + popstackmark(&sm); + + len = cvtnum(result); + + if (likely(!(flag & EXP_QUOTED))) + recordregion(begoff, begoff + len, 0); +} + + +/* + * Expand stuff in backwards quotes. + */ + +STATIC void +expbackq(union node *cmd, int flag) +{ + struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest; + int startloc; + char const *syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX; + struct stackmark smark; + + INTOFF; + startloc = expdest - (char *)stackblock(); + pushstackmark(&smark, startloc); + evalbackcmd(cmd, (struct backcmd *) &in); + popstackmark(&smark); + + p = in.buf; + i = in.nleft; + if (i == 0) + goto read; + for (;;) { + memtodest(p, i, syntax, flag & QUOTES_ESC); +read: + if (in.fd < 0) + break; + do { + i = read(in.fd, buf, sizeof buf); + } while (i < 0 && errno == EINTR); + TRACE(("expbackq: read returns %d\n", i)); + if (i <= 0) + break; + p = buf; + } + + if (in.buf) + ckfree(in.buf); + if (in.fd >= 0) { + close(in.fd); + back_exitstatus = waitforjob(in.jp); + } + INTON; + + /* Eat all trailing newlines */ + dest = expdest; + for (; dest > (char *)stackblock() && dest[-1] == '\n';) + STUNPUTC(dest); + expdest = dest; + + if (!(flag & EXP_QUOTED)) + recordregion(startloc, dest - (char *)stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - (char *)stackblock()) - startloc, + (dest - (char *)stackblock()) - startloc, + stackblock() + startloc)); +} + + +STATIC char * +scanleft( + char *startp, char *rmesc, char *rmescend, char *str, int quotes, + int zero +) { + char *loc; + char *loc2; + char c; + + loc = startp; + loc2 = rmesc; + do { + int match; + const char *s = loc2; + c = *loc2; + if (zero) { + *loc2 = '\0'; + s = rmesc; + } + match = pmatch(str, s); + *loc2 = c; + if (match) + return loc; + if (quotes && *loc == (char)CTLESC) + loc++; + loc++; + loc2++; + } while (c); + return 0; +} + + +STATIC char * +scanright( + char *startp, char *rmesc, char *rmescend, char *str, int quotes, + int zero +) { + int esc = 0; + char *loc; + char *loc2; + + for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) { + int match; + char c = *loc2; + const char *s = loc2; + if (zero) { + *loc2 = '\0'; + s = rmesc; + } + match = pmatch(str, s); + *loc2 = c; + if (match) + return loc; + loc--; + if (quotes) { + if (--esc < 0) { + esc = esclen(startp, loc); + } + if (esc % 2) { + esc--; + loc--; + } + } + } + return 0; +} + +STATIC const char * +subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int flag) +{ + int quotes = flag & QUOTES_ESC; + char *startp; + char *loc; + struct nodelist *saveargbackq = argbackq; + int amount; + char *rmesc, *rmescend; + int zero; + char *(*scan)(char *, char *, char *, char *, int , int); + + argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ? + (flag & EXP_QUOTED ? EXP_QPAT : EXP_CASE) : 0)); + STPUTC('\0', expdest); + argbackq = saveargbackq; + startp = stackblock() + startloc; + + switch (subtype) { + case VSASSIGN: + setvar(str, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + return startp; + + case VSQUESTION: + varunset(p, str, startp, varflags); + /* NOTREACHED */ + } + + subtype -= VSTRIMRIGHT; +#ifdef DEBUG + if (subtype < 0 || subtype > 3) + abort(); +#endif + + rmesc = startp; + rmescend = stackblock() + strloc; + if (quotes) { + rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); + if (rmesc != startp) { + rmescend = expdest; + startp = stackblock() + startloc; + } + } + rmescend--; + str = stackblock() + strloc; + preglob(str, 0); + + /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ + zero = subtype >> 1; + /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ + scan = (subtype & 1) ^ zero ? scanleft : scanright; + + loc = scan(startp, rmesc, rmescend, str, quotes, zero); + if (loc) { + if (zero) { + memmove(startp, loc, str - loc); + loc = startp + (str - loc) - 1; + } + *loc = '\0'; + amount = loc - expdest; + STADJUST(amount, expdest); + } + return loc; +} + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ +STATIC char * +evalvar(char *p, int flag) +{ + int subtype; + int varflags; + char *var; + int patloc; + int c; + int startloc; + ssize_t varlen; + int easy; + int quoted; + + varflags = *p++; + subtype = varflags & VSTYPE; + + if (!subtype) + sh_error("Bad substitution"); + + quoted = flag & EXP_QUOTED; + var = p; + easy = (!quoted || (*var == '@' && shellparam.nparam)); + startloc = expdest - (char *)stackblock(); + p = strchr(p, '=') + 1; + +again: + varlen = varvalue(var, varflags, flag); + if (varflags & VSNUL) + varlen--; + + if (subtype == VSPLUS) { + varlen = -1 - varlen; + goto vsplus; + } + + if (subtype == VSMINUS) { +vsplus: + if (varlen < 0) { + argstr(p, flag | EXP_TILDE | EXP_WORD); + goto end; + } + if (easy) + goto record; + goto end; + } + + if (subtype == VSASSIGN || subtype == VSQUESTION) { + if (varlen < 0) { + if (subevalvar(p, var, 0, subtype, startloc, + varflags, flag & ~QUOTES_ESC)) { + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; + } + goto end; + } + if (easy) + goto record; + goto end; + } + + if (varlen < 0 && uflag) + varunset(p, var, 0, 0); + + if (subtype == VSLENGTH) { + cvtnum(varlen > 0 ? varlen : 0); + goto record; + } + + if (subtype == VSNORMAL) { + if (!easy) + goto end; +record: + recordregion(startloc, expdest - (char *)stackblock(), quoted); + goto end; + } + +#ifdef DEBUG + switch (subtype) { + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + break; + default: + abort(); + } +#endif + + if (varlen >= 0) { + /* + * Terminate the string and start recording the pattern + * right after it + */ + STPUTC('\0', expdest); + patloc = expdest - (char *)stackblock(); + if (subevalvar(p, NULL, patloc, subtype, + startloc, varflags, flag) == 0) { + int amount = expdest - ( + (char *)stackblock() + patloc - 1 + ); + STADJUST(-amount, expdest); + } + /* Remove any recorded regions beyond start of variable */ + removerecordregions(startloc); + goto record; + } + +end: + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = (signed char)*p++) == CTLESC) + p++; + else if (c == CTLBACKQ) { + if (varlen >= 0) + argbackq = argbackq->next; + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + +/* + * Put a string on the stack. + */ + +STATIC void +memtodest(const char *p, size_t len, const char *syntax, int quotes) { + char *q; + + if (unlikely(!len)) + return; + + q = makestrspace(len * 2, expdest); + + do { + int c = (signed char)*p++; + if (c) { + if ((quotes & QUOTES_ESC) && + ((syntax[c] == CCTL) || + (((quotes & EXP_FULL) || syntax != BASESYNTAX) && + syntax[c] == CBACK))) + USTPUTC(CTLESC, q); + } else if (!(quotes & QUOTES_KEEPNUL)) + continue; + USTPUTC(c, q); + } while (--len); + + expdest = q; +} + + +STATIC size_t +strtodest(p, syntax, quotes) + const char *p; + const char *syntax; + int quotes; +{ + size_t len = strlen(p); + memtodest(p, len, syntax, quotes); + return len; +} + + + +/* + * Add the value of a specialized variable to the stack string. + */ + +STATIC ssize_t +varvalue(char *name, int varflags, int flags) +{ + int num; + char *p; + int i; + int sep; + char sepc; + char **ap; + char const *syntax; + int quoted = flags & EXP_QUOTED; + int subtype = varflags & VSTYPE; + int discard = subtype == VSPLUS || subtype == VSLENGTH; + int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; + ssize_t len = 0; + + sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0; + syntax = quoted ? DQSYNTAX : BASESYNTAX; + + switch (*name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = exitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; + if (num == 0) + return -1; +numvar: + len = cvtnum(num); + break; + case '-': + p = makestrspace(NOPTS, expdest); + for (i = NOPTS - 1; i >= 0; i--) { + if (optlist[i]) { + USTPUTC(optletters[i], p); + len++; + } + } + expdest = p; + break; + case '@': + if (sep) + goto param; + /* fall through */ + case '*': + sep = ifsset() ? ifsval()[0] : ' '; +param: + if (!(ap = shellparam.p)) + return -1; + sepc = sep; + while ((p = *ap++)) { + len += strtodest(p, syntax, quotes); + + if (*ap && sep) { + len++; + memtodest(&sepc, 1, syntax, quotes); + } + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + num = atoi(name); + if (num < 0 || num > shellparam.nparam) + return -1; + p = num ? shellparam.p[num - 1] : arg0; + goto value; + default: + p = lookupvar(name); +value: + if (!p) + return -1; + + len = strtodest(p, syntax, quotes); + break; + } + + if (discard) + STADJUST(-len, expdest); + return len; +} + + + +/* + * Record the fact that we have to scan this region of the + * string for IFS characters. + */ + +void +recordregion(int start, int end, int nulonly) +{ + struct ifsregion *ifsp; + + if (ifslastp == NULL) { + ifsp = &ifsfirst; + } else { + INTOFF; + ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); + ifsp->next = NULL; + ifslastp->next = ifsp; + INTON; + } + ifslastp = ifsp; + ifslastp->begoff = start; + ifslastp->endoff = end; + ifslastp->nulonly = nulonly; +} + + + +/* + * Break the argument string into pieces based upon IFS and add the + * strings to the argument list. The regions of the string to be + * searched for IFS characters have been stored by recordregion. + */ +void +ifsbreakup(char *string, struct arglist *arglist) +{ + struct ifsregion *ifsp; + struct strlist *sp; + char *start; + char *p; + char *q; + const char *ifs, *realifs; + int ifsspc; + int nulonly; + + + start = string; + if (ifslastp != NULL) { + ifsspc = 0; + nulonly = 0; + realifs = ifsset() ? ifsval() : defifs; + ifsp = &ifsfirst; + do { + p = string + ifsp->begoff; + nulonly = ifsp->nulonly; + ifs = nulonly ? nullstr : realifs; + ifsspc = 0; + while (p < string + ifsp->endoff) { + q = p; + if (*p == (char)CTLESC) + p++; + if (strchr(ifs, *p)) { + if (!nulonly) + ifsspc = (strchr(defifs, *p) != NULL); + /* Ignore IFS whitespace at start */ + if (q == start && ifsspc) { + p++; + start = p; + continue; + } + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + p++; + if (!nulonly) { + for (;;) { + if (p >= string + ifsp->endoff) { + break; + } + q = p; + if (*p == (char)CTLESC) + p++; + if (strchr(ifs, *p) == NULL ) { + p = q; + break; + } else if (strchr(defifs, *p) == NULL) { + if (ifsspc) { + p++; + ifsspc = 0; + } else { + p = q; + break; + } + } else + p++; + } + } + start = p; + } else + p++; + } + } while ((ifsp = ifsp->next) != NULL); + if (nulonly) + goto add; + } + + if (!*start) + return; + +add: + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; +} + +void ifsfree(void) +{ + struct ifsregion *p = ifsfirst.next; + + if (!p) + goto out; + + INTOFF; + do { + struct ifsregion *ifsp; + ifsp = p->next; + ckfree(p); + p = ifsp; + } while (p); + ifsfirst.next = NULL; + INTON; + +out: + ifslastp = NULL; +} + + + +/* + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. + */ + +#ifdef HAVE_GLOB +STATIC void +expandmeta(str, flag) + struct strlist *str; + int flag; +{ + /* TODO - EXP_REDIR */ + + while (str) { + const char *p; + glob_t pglob; + int i; + + if (fflag) + goto nometa; + INTOFF; + p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); + i = glob(p, GLOB_NOMAGIC, 0, &pglob); + if (p != str->text) + ckfree(p); + switch (i) { + case 0: + if (!(pglob.gl_flags & GLOB_MAGCHAR)) + goto nometa2; + addglob(&pglob); + globfree(&pglob); + INTON; + break; + case GLOB_NOMATCH: +nometa2: + globfree(&pglob); + INTON; +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + break; + default: /* GLOB_NOSPACE */ + sh_error("Out of space"); + } + str = str->next; + } +} + + +/* + * Add the result of glob(3) to the list. + */ + +STATIC void +addglob(pglob) + const glob_t *pglob; +{ + char **p = pglob->gl_pathv; + + do { + addfname(*p); + } while (*++p); +} + + +#else /* HAVE_GLOB */ +STATIC char *expdir; + + +STATIC void +expandmeta(struct strlist *str, int flag) +{ + static const char metachars[] = { + '*', '?', '[', 0 + }; + /* TODO - EXP_REDIR */ + + while (str) { + struct strlist **savelastp; + struct strlist *sp; + char *p; + + if (fflag) + goto nometa; + if (!strpbrk(str->text, metachars)) + goto nometa; + savelastp = exparg.lastp; + + INTOFF; + p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); + { + int i = strlen(str->text); + expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ + } + + expmeta(expdir, p); + ckfree(expdir); + if (p != str->text) + ckfree(p); + INTON; + if (exparg.lastp == savelastp) { + /* + * no matches + */ +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + } else { + *exparg.lastp = NULL; + *savelastp = sp = expsort(*savelastp); + while (sp->next != NULL) + sp = sp->next; + exparg.lastp = &sp->next; + } + str = str->next; + } +} + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +STATIC void +expmeta(char *enddir, char *name) +{ + char *p; + const char *cp; + char *start; + char *endname; + int metaflag; + struct stat64 statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + int esc; + + metaflag = 0; + start = name; + for (p = name; esc = 0, *p; p += esc + 1) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + char *q = p + 1; + if (*q == '!') + q++; + for (;;) { + if (*q == '\\') + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else { + if (*p == '\\') + esc++; + if (p[esc] == '/') { + if (metaflag) + break; + start = p + esc + 1; + } + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + p = name; + do { + if (*p == '\\') + p++; + *enddir++ = *p; + } while (*p++); + if (metaflag == 0 || lstat64(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (name < start) { + p = name; + do { + if (*p == '\\') + p++; + *enddir++ = *p++; + } while (p < start); + } + if (enddir == expdir) { + cp = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + cp = "/"; + } else { + cp = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(cp)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname = '\0'; + endname += esc + 1; + } + matchdot = 0; + p = start; + if (*p == '\\') + p++; + if (*p == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (pmatch(start, dp->d_name)) { + if (atend) { + scopy(dp->d_name, enddir); + addfname(expdir); + } else { + for (p = enddir, cp = dp->d_name; + (*p++ = *cp++) != '\0';) + continue; + p[-1] = '/'; + expmeta(p, endname); + } + } + } + closedir(dirp); + if (! atend) + endname[-esc - 1] = esc ? '\\' : '/'; +} +#endif /* HAVE_GLOB */ + + +/* + * Add a file name to the list. + */ + +STATIC void +addfname(char *name) +{ + struct strlist *sp; + + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = sstrdup(name); + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} + + +#ifndef HAVE_GLOB +/* + * Sort the results of file name expansion. It calculates the number of + * strings to sort and then calls msort (short for merge sort) to do the + * work. + */ + +STATIC struct strlist * +expsort(struct strlist *str) +{ + int len; + struct strlist *sp; + + len = 0; + for (sp = str ; sp ; sp = sp->next) + len++; + return msort(str, len); +} + + +STATIC struct strlist * +msort(struct strlist *list, int len) +{ + struct strlist *p, *q = NULL; + struct strlist **lpp; + int half; + int n; + + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { + if (strcmp(p->text, q->text) < 0) { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} +#endif + + +/* + * Returns true if the pattern matches the string. + */ + +STATIC inline int +patmatch(char *pattern, const char *string) +{ + return pmatch(preglob(pattern, 0), string); +} + + +#ifndef HAVE_FNMATCH +STATIC int ccmatch(const char *p, int chr, const char **r) +{ + static const struct class { + char name[10]; + int (*fn)(int); + } classes[] = { + { .name = ":alnum:]", .fn = isalnum }, + { .name = ":cntrl:]", .fn = iscntrl }, + { .name = ":lower:]", .fn = islower }, + { .name = ":space:]", .fn = isspace }, + { .name = ":alpha:]", .fn = isalpha }, + { .name = ":digit:]", .fn = isdigit }, + { .name = ":print:]", .fn = isprint }, + { .name = ":upper:]", .fn = isupper }, + { .name = ":blank:]", .fn = isblank }, + { .name = ":graph:]", .fn = isgraph }, + { .name = ":punct:]", .fn = ispunct }, + { .name = ":xdigit:]", .fn = isxdigit }, + }; + const struct class *class, *end; + + end = classes + sizeof(classes) / sizeof(classes[0]); + for (class = classes; class < end; class++) { + const char *q; + + q = prefix(p, class->name); + if (!q) + continue; + *r = q; + return class->fn(chr); + } + + *r = 0; + return 0; +} + +STATIC int +pmatch(const char *pattern, const char *string) +{ + const char *p, *q; + char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case '\\': + if (*p) { + c = *p++; + } + goto dft; + case '?': + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + while (c == '*') + c = *++p; + if (c != '\\' && c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (*q == '\0') + return 0; + q++; + } + } + do { + if (pmatch(p, q)) + return 1; + } while (*q++ != '\0'); + return 0; + case '[': { + const char *startp; + int invert, found; + char chr; + + startp = p; + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q++; + if (chr == '\0') + return 0; + c = *p++; + do { + if (!c) { + p = startp; + c = *p; + goto dft; + } + if (c == '[') { + const char *r; + + found |= !!ccmatch(p, chr, &r); + if (r) { + p = r; + continue; + } + } else if (c == '\\') + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + if (*p == '\\') + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + break; + } +dft: default: + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; +} +#endif + + + +/* + * Remove any CTLESC characters from a string. + */ + +char * +_rmescapes(char *str, int flag) +{ + char *p, *q, *r; + unsigned inquotes; + int notescaped; + int globbing; + + p = strpbrk(str, qchars); + if (!p) { + return str; + } + q = p; + r = str; + if (flag & RMESCAPE_ALLOC) { + size_t len = p - str; + size_t fulllen = len + strlen(p) + 1; + + if (flag & RMESCAPE_GROW) { + int strloc = str - (char *)stackblock(); + + r = makestrspace(fulllen, expdest); + str = (char *)stackblock() + strloc; + p = str + len; + } else if (flag & RMESCAPE_HEAP) { + r = ckmalloc(fulllen); + } else { + r = stalloc(fulllen); + } + q = r; + if (len > 0) { + q = mempcpy(q, str, len); + } + } + inquotes = 0; + globbing = flag & RMESCAPE_GLOB; + notescaped = globbing; + while (*p) { + if (*p == (char)CTLQUOTEMARK) { + inquotes = ~inquotes; + p++; + notescaped = globbing; + continue; + } + if (*p == (char)CTLESC) { + p++; + if (notescaped) + *q++ = '\\'; + } else if (*p == '\\' && !inquotes) { + /* naked back slash */ + notescaped = 0; + goto copy; + } + notescaped = globbing; +copy: + *q++ = *p++; + } + *q = '\0'; + if (flag & RMESCAPE_GROW) { + expdest = r; + STADJUST(q - r + 1, expdest); + } + return r; +} + + + +/* + * See if a pattern matches in a case statement. + */ + +int +casematch(union node *pattern, char *val) +{ + struct stackmark smark; + int result; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + STACKSTRNUL(expdest); + ifsfree(); + result = patmatch(stackblock(), val); + popstackmark(&smark); + return result; +} + +/* + * Our own itoa(). + */ + +STATIC int +cvtnum(intmax_t num) +{ + int len = max_int_length(sizeof(num)); + + expdest = makestrspace(len, expdest); + len = fmtstr(expdest, len, "%" PRIdMAX, num); + STADJUST(len, expdest); + return len; +} + +STATIC void +varunset(const char *end, const char *var, const char *umsg, int varflags) +{ + const char *msg; + const char *tail; + + tail = nullstr; + msg = "parameter not set"; + if (umsg) { + if (*end == (char)CTLENDVAR) { + if (varflags & VSNUL) + tail = " or null"; + } else + msg = umsg; + } + sh_error("%.*s: %s%s", end - var - 1, var, msg, tail); +} + +#ifdef mkinit + +INCLUDE "expand.h" + +RESET { + ifsfree(); +} + +#endif diff --git a/usr/dash/expand.h b/usr/dash/expand.h new file mode 100644 index 0000000..9cf1276 --- /dev/null +++ b/usr/dash/expand.h @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)expand.h 8.2 (Berkeley) 5/4/95 + */ + +#ifndef DASH_STRLIST_H +#define DASH_STRLIST_H + +#include <inttypes.h> + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +/* + * expandarg() flags + */ +#define EXP_FULL 0x1 /* perform word splitting & file globbing */ +#define EXP_TILDE 0x2 /* do normal tilde expansion */ +#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ +#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ +#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ +#define EXP_QPAT 0x20 /* pattern in quoted parameter expansion */ +#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */ +#define EXP_WORD 0x80 /* expand word in parameter expansion */ +#define EXP_QUOTED 0x100 /* expand word in double quotes */ + + +union node; +void expandarg(union node *, struct arglist *, int); +void expari(int); +#define rmescapes(p) _rmescapes((p), 0) +char *_rmescapes(char *, int); +int casematch(union node *, char *); +void recordregion(int, int, int); +void removerecordregions(int); +void ifsbreakup(char *, struct arglist *); +void ifsfree(void); + +/* From arith.y */ +intmax_t arith(const char *); +int expcmd(int , char **); +#ifdef USE_LEX +void arith_lex_reset(void); +#else +#define arith_lex_reset() +#endif +int yylex(void); + +#endif /* DASH_STRLIST_H */ diff --git a/usr/dash/funcs/cmv b/usr/dash/funcs/cmv new file mode 100644 index 0000000..91a67c5 --- /dev/null +++ b/usr/dash/funcs/cmv @@ -0,0 +1,47 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)cmv 8.2 (Berkeley) 5/4/95 + +# Conditional move--don't replace an existing file. + +cmv() { + if test $# != 2 + then echo "cmv: arg count" + return 2 + fi + if test -f "$2" -o -w "$2" + then echo "$2 exists" + return 2 + fi + /bin/mv "$1" "$2" +} diff --git a/usr/dash/funcs/dirs b/usr/dash/funcs/dirs new file mode 100644 index 0000000..7d840eb --- /dev/null +++ b/usr/dash/funcs/dirs @@ -0,0 +1,71 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)dirs 8.2 (Berkeley) 5/4/95 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/usr/dash/funcs/kill b/usr/dash/funcs/kill new file mode 100644 index 0000000..c5df95f --- /dev/null +++ b/usr/dash/funcs/kill @@ -0,0 +1,47 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)kill 8.2 (Berkeley) 5/4/95 + +# Convert job names to process ids and then run /bin/kill. + +kill() { + local args x + args= + for x in "$@" + do case $x in + %*) x=`jobid "$x"` ;; + esac + args="$args $x" + done + /bin/kill $args +} diff --git a/usr/dash/funcs/login b/usr/dash/funcs/login new file mode 100644 index 0000000..215e535 --- /dev/null +++ b/usr/dash/funcs/login @@ -0,0 +1,36 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)login 8.2 (Berkeley) 5/4/95 + +# replaces the login builtin in the BSD shell +login () exec login "$@" diff --git a/usr/dash/funcs/newgrp b/usr/dash/funcs/newgrp new file mode 100644 index 0000000..ec0e7e5 --- /dev/null +++ b/usr/dash/funcs/newgrp @@ -0,0 +1,35 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)newgrp 8.2 (Berkeley) 5/4/95 + +newgrp() exec newgrp "$@" diff --git a/usr/dash/funcs/popd b/usr/dash/funcs/popd new file mode 100644 index 0000000..3b1ab46 --- /dev/null +++ b/usr/dash/funcs/popd @@ -0,0 +1,71 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)popd 8.2 (Berkeley) 5/4/95 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/usr/dash/funcs/pushd b/usr/dash/funcs/pushd new file mode 100644 index 0000000..483d358 --- /dev/null +++ b/usr/dash/funcs/pushd @@ -0,0 +1,71 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)pushd 8.2 (Berkeley) 5/4/95 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/usr/dash/funcs/suspend b/usr/dash/funcs/suspend new file mode 100644 index 0000000..4484467 --- /dev/null +++ b/usr/dash/funcs/suspend @@ -0,0 +1,39 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)suspend 8.2 (Berkeley) 5/4/95 + +suspend() { + local - + set +j + kill -TSTP 0 +} diff --git a/usr/dash/gendeps.pl b/usr/dash/gendeps.pl new file mode 100755 index 0000000..e6797de --- /dev/null +++ b/usr/dash/gendeps.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl +# +# Generate dependencies for *generated* header files. Generated +# header files have to use #include "foo.h" syntax. +# + +($src, $obj, @build_headers) = @ARGV; +%build_headers = map { $_ => 1 } @build_headers; + +open(GENDEPS, "> $obj/.gendeps\0") + or die "$0: Cannot create $obj/.gendeps: $!\n"; + +opendir(DIR, $src) or die "$0: Cannot opendir $src: $!\n"; +while ( defined($file = readdir(DIR)) ) { + if ( $file =~ /^(.*)\.c$/ ) { + $basename = $1; + @hdrs = (); + open(FILE, "< $src/$file\0") + or die "$0: Cannot open $src/$file: $!\n"; + while ( defined($line = <FILE>) ) { + if ( $line =~ /^\s*\#\s*include\s+\"(.*)\"/ ) { + $header = $1; + + if ( $build_headers{$header} ) { + push(@hdrs, "\$(obj)/$header"); + } + } + } + close(FILE); + + if (scalar(@hdrs)) { + print GENDEPS "\$(obj)/$basename.o: ", join(' ', @hdrs), "\n"; + } + } +} + +closedir(DIR); +close(GENDEPS); diff --git a/usr/dash/hetio.c b/usr/dash/hetio.c new file mode 100644 index 0000000..f7d175f --- /dev/null +++ b/usr/dash/hetio.c @@ -0,0 +1,397 @@ +/* + * Termios command line History and Editting for NetBSD sh (ash) + * Copyright (c) 1999 + * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> + * Etc: Dave Cinege <dcinege@psychosis.com> + * + * You may use this code as you wish, so long as the original author(s) + * are attributed in any redistributions of the source code. + * This code is 'as is' with no warranty. + * This code may safely be consumed by a BSD or GPL license. + * + * v 0.5 19990328 Initial release + * + * Future plans: Simple file and path name completion. (like BASH) + * + */ + +/* +Usage and Known bugs: + Terminal key codes are not extensive, and more will probably + need to be added. This version was created on Debian GNU/Linux 2.x. + Delete, Backspace, Home, End, and the arrow keys were tested + to work in an Xterm and console. Ctrl-A also works as Home. + Ctrl-E also works as End. Ctrl-D and Ctrl-U perform their respective + functions. The binary size increase is <3K. + + Editting will not display correctly for lines greater then the + terminal width. (more then one line.) However, history will. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <ctype.h> +#include <sys/ioctl.h> + +#include "input.h" +#include "output.h" + +#include "hetio.h" + + +#define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ + +#define ESC 27 +#define DEL 127 + +static struct history *his_front = NULL; /* First element in command line list */ +static struct history *his_end = NULL; /* Last element in command line list */ +static struct termios old_term, new_term; /* Current termio and the previous termio before starting ash */ + +static int history_counter = 0; /* Number of commands in history list */ +static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ +static int hetio_inter = 0; + +struct history +{ + char *s; + struct history *p; + struct history *n; +}; + + +void input_delete (int); +void input_home (int *); +void input_end (int *, int); +void input_backspace (int *, int *); + + + +void hetio_init(void) +{ + hetio_inter = 1; +} + + +void hetio_reset_term(void) +{ + if (reset_term) + tcsetattr(1, TCSANOW, &old_term); +} + + +void setIO(struct termios *new, struct termios *old) /* Set terminal IO to canonical mode, and save old term settings. */ +{ + tcgetattr(0, old); + memcpy(new, old, sizeof(*new)); + new->c_cc[VMIN] = 1; + new->c_cc[VTIME] = 0; + new->c_lflag &= ~ICANON; /* unbuffered input */ + new->c_lflag &= ~ECHO; + tcsetattr(0, TCSANOW, new); +} + +void input_home(int *cursor) /* Command line input routines */ +{ + while (*cursor > 0) { + out1c('\b'); + --*cursor; + } + flushout(out1); +} + + +void input_delete(int cursor) +{ + int j = 0; + + memmove(parsenextc + cursor, parsenextc + cursor + 1, + BUFSIZ - cursor - 1); + for (j = cursor; j < (BUFSIZ - 1); j++) { + if (!*(parsenextc + j)) + break; + else + out1c(*(parsenextc + j)); + } + + out1str(" \b"); + + while (j-- > cursor) + out1c('\b'); + flushout(out1); +} + + +void input_end(int *cursor, int len) +{ + while (*cursor < len) { + out1str("\033[C"); + ++*cursor; + } + flushout(out1); +} + + +void +input_backspace(int *cursor, int *len) +{ + int j = 0; + + if (*cursor > 0) { + out1str("\b \b"); + --*cursor; + memmove(parsenextc + *cursor, parsenextc + *cursor + 1, + BUFSIZ - *cursor + 1); + + for (j = *cursor; j < (BUFSIZ - 1); j++) { + if (!*(parsenextc + j)) + break; + else + out1c(*(parsenextc + j)); + } + + out1str(" \b"); + + while (j-- > *cursor) + out1c('\b'); + + --*len; + flushout(out1); + } +} + +int hetio_read_input(int fd) +{ + int nr = 0; + + /* Are we an interactive shell? */ + if (!hetio_inter || fd) { + return -255; + } else { + int len = 0; + int j = 0; + int cursor = 0; + int break_out = 0; + int ret = 0; + char c = 0; + struct history *hp = his_end; + + if (!reset_term) { + setIO(&new_term, &old_term); + reset_term = 1; + } else { + tcsetattr(0, TCSANOW, &new_term); + } + + memset(parsenextc, 0, BUFSIZ); + + while (1) { + if ((ret = read(fd, &c, 1)) < 1) + return ret; + + switch (c) { + case 1: /* Control-A Beginning of line */ + input_home(&cursor); + break; + case 5: /* Control-E EOL */ + input_end(&cursor, len); + break; + case 4: /* Control-D */ + if (!len) + exitshell(0); + break; + case 21: /* Control-U */ + /* Return to begining of line. */ + for (; cursor > 0; cursor--) + out1c('\b'); + /* Erase old command. */ + for (j = 0; j < len; j++) { + /* + * Clear buffer while we're at + * it. + */ + parsenextc[j] = 0; + out1c(' '); + } + /* return to begining of line */ + for (; len > 0; len--) + out1c('\b'); + flushout(out1); + break; + case '\b': /* Backspace */ + case DEL: + input_backspace(&cursor, &len); + break; + case '\n': /* Enter */ + *(parsenextc + len++ + 1) = c; + out1c(c); + flushout(out1); + break_out = 1; + break; + case ESC: /* escape sequence follows */ + if ((ret = read(fd, &c, 1)) < 1) + return ret; + + if (c == '[' ) { /* 91 */ + if ((ret = read(fd, &c, 1)) < 1) + return ret; + + switch (c) { + case 'A': + if (hp && hp->p) { /* Up */ + hp = hp->p; + goto hop; + } + break; + case 'B': + if (hp && hp->n && hp->n->s) { /* Down */ + hp = hp->n; + goto hop; + } + break; + +hop: /* hop */ + len = strlen(parsenextc); + + for (; cursor > 0; cursor--) /* return to begining of line */ + out1c('\b'); + + for (j = 0; j < len; j++) /* erase old command */ + out1c(' '); + + for (; j > 0; j--) /* return to begining of line */ + out1c('\b'); + + strcpy (parsenextc, hp->s); /* write new command */ + len = strlen (hp->s); + out1str(parsenextc); + flushout(out1); + cursor = len; + break; + case 'C': /* Right */ + if (cursor < len) { + out1str("\033[C"); + cursor++; + flushout(out1); + } + break; + case 'D': /* Left */ + if (cursor > 0) { + out1str("\033[D"); + cursor--; + flushout(out1); + } + break; + case '3': /* Delete */ + if (cursor != len) { + input_delete(cursor); + len--; + } + break; + case '1': /* Home (Ctrl-A) */ + input_home(&cursor); + break; + case '4': /* End (Ctrl-E) */ + input_end(&cursor, len); + break; + } + if (c == '1' || c == '3' || c == '4') + if ((ret = read(fd, &c, 1)) < 1) + return ret; /* read 126 (~) */ + } + + if (c == 'O') { /* 79 */ + if ((ret = read(fd, &c, 1)) < 1) + return ret; + switch (c) { + case 'H': /* Home (xterm) */ + input_home(&cursor); + break; + case 'F': /* End (xterm_ */ + input_end(&cursor, len); + break; + } + } + + c = 0; + break; + + default: /* If it's regular input, do the normal thing */ + if (!isprint(c)) /* Skip non-printable characters */ + break; + + if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ + break; + + len++; + + if (cursor == (len - 1)) { /* Append if at the end of the line */ + *(parsenextc + cursor) = c; + } else { /* Insert otherwise */ + memmove(parsenextc + cursor + 1, parsenextc + cursor, + len - cursor - 1); + + *(parsenextc + cursor) = c; + + for (j = cursor; j < len; j++) + out1c(*(parsenextc + j)); + for (; j > cursor; j--) + out1str("\033[D"); + } + + cursor++; + out1c(c); + flushout(out1); + break; + } + + if (break_out) /* Enter is the command terminator, no more input. */ + break; + } + + nr = len + 1; + tcsetattr(0, TCSANOW, &old_term); + + if (*(parsenextc)) { /* Handle command history log */ + struct history *h = his_end; + + if (!h) { /* No previous history */ + h = his_front = malloc(sizeof (struct history)); + h->n = malloc(sizeof (struct history)); + h->p = NULL; + h->s = strdup(parsenextc); + + h->n->p = h; + h->n->n = NULL; + h->n->s = NULL; + his_end = h->n; + history_counter++; + } else { /* Add a new history command */ + + h->n = malloc(sizeof (struct history)); + + h->n->p = h; + h->n->n = NULL; + h->n->s = NULL; + h->s = strdup(parsenextc); + his_end = h->n; + + if (history_counter >= MAX_HISTORY) { /* After max history, remove the last known command */ + struct history *p = his_front->n; + + p->p = NULL; + free(his_front->s); + free(his_front); + his_front = p; + } else { + history_counter++; + } + } + } + } + + return nr; +} diff --git a/usr/dash/hetio.h b/usr/dash/hetio.h new file mode 100644 index 0000000..5f49713 --- /dev/null +++ b/usr/dash/hetio.h @@ -0,0 +1,22 @@ +/* + * Termios command line History and Editting for NetBSD sh (ash) + * Copyright (c) 1999 + * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> + * Etc: Dave Cinege <dcinege@psychosis.com> + * + * You may use this code as you wish, so long as the original author(s) + * are attributed in any redistributions of the source code. + * This code is 'as is' with no warranty. + * This code may safely be consumed by a BSD or GPL license. + * + * v 0.5 19990328 Initial release + * + * Future plans: Simple file and path name completion. (like BASH) + * + */ + +void hetio_init(void); +int hetio_read_input(int fd); +void hetio_reset_term(void); + +extern int hetio_inter; diff --git a/usr/dash/histedit.c b/usr/dash/histedit.c new file mode 100644 index 0000000..b27d629 --- /dev/null +++ b/usr/dash/histedit.c @@ -0,0 +1,494 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <sys/param.h> +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +/* + * Editline and history functions (and glue). + */ +#include "shell.h" +#include "parser.h" +#include "var.h" +#include "options.h" +#include "main.h" +#include "output.h" +#include "mystring.h" +#include "error.h" +#ifndef SMALL +#include "myhistedit.h" +#include "eval.h" +#include "memalloc.h" + +#define MAXHISTLOOPS 4 /* max recursions through fc */ +#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ + +History *hist; /* history cookie */ +EditLine *el; /* editline cookie */ +int displayhist; +static FILE *el_in, *el_out; + +STATIC const char *fc_replace(const char *, char *, char *); + +#ifdef DEBUG +extern FILE *tracefile; +#endif + +/* + * Set history and editing status. Called whenever the status may + * have changed (figures out what to do). + */ +void +histedit(void) +{ + FILE *el_err; + +#define editing (Eflag || Vflag) + + if (iflag) { + if (!hist) { + /* + * turn history on + */ + INTOFF; + hist = history_init(); + INTON; + + if (hist != NULL) + sethistsize(histsizeval()); + else + out2str("sh: can't initialize history\n"); + } + if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ + /* + * turn editing on + */ + INTOFF; + if (el_in == NULL) + el_in = fdopen(0, "r"); + if (el_out == NULL) + el_out = fdopen(2, "w"); + if (el_in == NULL || el_out == NULL) + goto bad; + el_err = el_out; +#if DEBUG + if (tracefile) + el_err = tracefile; +#endif + el = el_init(arg0, el_in, el_out, el_err); + if (el != NULL) { + if (hist) + el_set(el, EL_HIST, history, hist); + el_set(el, EL_PROMPT, getprompt); + } else { +bad: + out2str("sh: can't initialize editing\n"); + } + INTON; + } else if (!editing && el) { + INTOFF; + el_end(el); + el = NULL; + INTON; + } + if (el) { + if (Vflag) + el_set(el, EL_EDITOR, "vi"); + else if (Eflag) + el_set(el, EL_EDITOR, "emacs"); + el_source(el, NULL); + } + } else { + INTOFF; + if (el) { /* no editing if not interactive */ + el_end(el); + el = NULL; + } + if (hist) { + history_end(hist); + hist = NULL; + } + INTON; + } +} + + +void +sethistsize(const char *hs) +{ + int histsize; + HistEvent he; + + if (hist != NULL) { + if (hs == NULL || *hs == '\0' || + (histsize = atoi(hs)) < 0) + histsize = 100; + history(hist, &he, H_SETSIZE, histsize); + } +} + +void +setterm(const char *term) +{ + if (el != NULL && term != NULL) + if (el_set(el, EL_TERMINAL, term) != 0) { + outfmt(out2, "sh: Can't set terminal type %s\n", term); + outfmt(out2, "sh: Using dumb terminal settings.\n"); + } +} + +/* + * This command is provided since POSIX decided to standardize + * the Korn shell fc command. Oh well... + */ +int +histcmd(int argc, char **argv) +{ + int ch; + const char *editor = NULL; + HistEvent he; + int lflg = 0, nflg = 0, rflg = 0, sflg = 0; + int i, retval; + const char *firststr, *laststr; + int first, last, direction; + char *pat = NULL, *repl; /* ksh "fc old=new" crap */ + static int active = 0; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + char editfile[MAXPATHLEN + 1]; + FILE *efp; +#ifdef __GNUC__ + /* Avoid longjmp clobbering */ + (void) &editor; + (void) &lflg; + (void) &nflg; + (void) &rflg; + (void) &sflg; + (void) &firststr; + (void) &laststr; + (void) &pat; + (void) &repl; + (void) &efp; + (void) &argc; + (void) &argv; +#endif + + if (hist == NULL) + sh_error("history not active"); + + if (argc == 1) + sh_error("missing history argument"); + +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; optind = 1; /* initialize getopt */ +#endif + while (not_fcnumber(argv[optind]) && + (ch = getopt(argc, argv, ":e:lnrs")) != -1) + switch ((char)ch) { + case 'e': + editor = optionarg; + break; + case 'l': + lflg = 1; + break; + case 'n': + nflg = 1; + break; + case 'r': + rflg = 1; + break; + case 's': + sflg = 1; + break; + case ':': + sh_error("option -%c expects argument", optopt); + /* NOTREACHED */ + case '?': + default: + sh_error("unknown option: -%c", optopt); + /* NOTREACHED */ + } + argc -= optind, argv += optind; + + /* + * If executing... + */ + if (lflg == 0 || editor || sflg) { + lflg = 0; /* ignore */ + editfile[0] = '\0'; + /* + * Catch interrupts to reset active counter and + * cleanup temp files. + */ + if (setjmp(jmploc.loc)) { + active = 0; + if (*editfile) + unlink(editfile); + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + if (++active > MAXHISTLOOPS) { + active = 0; + displayhist = 0; + sh_error("called recursively too many times"); + } + /* + * Set editor. + */ + if (sflg == 0) { + if (editor == NULL && + (editor = bltinlookup("FCEDIT")) == NULL && + (editor = bltinlookup("EDITOR")) == NULL) + editor = DEFEDITOR; + if (editor[0] == '-' && editor[1] == '\0') { + sflg = 1; /* no edit */ + editor = NULL; + } + } + } + + /* + * If executing, parse [old=new] now + */ + if (lflg == 0 && argc > 0 && + ((repl = strchr(argv[0], '=')) != NULL)) { + pat = argv[0]; + *repl++ = '\0'; + argc--, argv++; + } + /* + * determine [first] and [last] + */ + switch (argc) { + case 0: + firststr = lflg ? "-16" : "-1"; + laststr = "-1"; + break; + case 1: + firststr = argv[0]; + laststr = lflg ? "-1" : argv[0]; + break; + case 2: + firststr = argv[0]; + laststr = argv[1]; + break; + default: + sh_error("too many args"); + /* NOTREACHED */ + } + /* + * Turn into event numbers. + */ + first = str_to_event(firststr, 0); + last = str_to_event(laststr, 1); + + if (rflg) { + i = last; + last = first; + first = i; + } + /* + * XXX - this should not depend on the event numbers + * always increasing. Add sequence numbers or offset + * to the history element in next (diskbased) release. + */ + direction = first < last ? H_PREV : H_NEXT; + + /* + * If editing, grab a temp file. + */ + if (editor) { + int fd; + INTOFF; /* easier */ + sprintf(editfile, "%s_shXXXXXX", _PATH_TMP); + if ((fd = mkstemp(editfile)) < 0) + sh_error("can't create temporary file %s", editfile); + if ((efp = fdopen(fd, "w")) == NULL) { + close(fd); + sh_error("can't allocate stdio buffer for temp"); + } + } + + /* + * Loop through selected history events. If listing or executing, + * do it now. Otherwise, put into temp file and call the editor + * after. + * + * The history interface needs rethinking, as the following + * convolutions will demonstrate. + */ + history(hist, &he, H_FIRST); + retval = history(hist, &he, H_NEXT_EVENT, first); + for (;retval != -1; retval = history(hist, &he, direction)) { + if (lflg) { + if (!nflg) + out1fmt("%5d ", he.num); + out1str(he.str); + } else { + const char *s = pat ? + fc_replace(he.str, pat, repl) : he.str; + + if (sflg) { + if (displayhist) { + out2str(s); + } + + evalstring(strcpy(stalloc(strlen(s) + 1), s), + 0); + if (displayhist && hist) { + /* + * XXX what about recursive and + * relative histnums. + */ + history(hist, &he, H_ENTER, s); + } + } else + fputs(s, efp); + } + /* + * At end? (if we were to lose last, we'd sure be + * messed up). + */ + if (he.num == last) + break; + } + if (editor) { + char *editcmd; + + fclose(efp); + editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); + sprintf(editcmd, "%s %s", editor, editfile); + /* XXX - should use no JC command */ + evalstring(editcmd, 0); + INTON; + readcmdfile(editfile); /* XXX - should read back - quick tst */ + unlink(editfile); + } + + if (lflg == 0 && active > 0) + --active; + if (displayhist) + displayhist = 0; + return 0; +} + +STATIC const char * +fc_replace(const char *s, char *p, char *r) +{ + char *dest; + int plen = strlen(p); + + STARTSTACKSTR(dest); + while (*s) { + if (*s == *p && strncmp(s, p, plen) == 0) { + while (*r) + STPUTC(*r++, dest); + s += plen; + *p = '\0'; /* so no more matches */ + } else + STPUTC(*s++, dest); + } + STACKSTRNUL(dest); + dest = grabstackstr(dest); + + return (dest); +} + +int +not_fcnumber(char *s) +{ + if (s == NULL) + return 0; + if (*s == '-') + s++; + return (!is_number(s)); +} + +int +str_to_event(const char *str, int last) +{ + HistEvent he; + const char *s = str; + int relative = 0; + int i, retval; + + retval = history(hist, &he, H_FIRST); + switch (*s) { + case '-': + relative = 1; + /*FALLTHROUGH*/ + case '+': + s++; + } + if (is_number(s)) { + i = atoi(s); + if (relative) { + while (retval != -1 && i--) { + retval = history(hist, &he, H_NEXT); + } + if (retval == -1) + retval = history(hist, &he, H_LAST); + } else { + retval = history(hist, &he, H_NEXT_EVENT, i); + if (retval == -1) { + /* + * the notion of first and last is + * backwards to that of the history package + */ + retval = history(hist, &he, + last ? H_FIRST : H_LAST); + } + } + if (retval == -1) + sh_error("history number %s not found (internal error)", + str); + } else { + /* + * pattern + */ + retval = history(hist, &he, H_PREV_STR, str); + if (retval == -1) + sh_error("history pattern not found: %s", str); + } + return (he.num); +} +#endif diff --git a/usr/dash/init.h b/usr/dash/init.h new file mode 100644 index 0000000..e026e86 --- /dev/null +++ b/usr/dash/init.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)init.h 8.2 (Berkeley) 5/4/95 + */ + +void init(void); +void reset(void); +void initshellproc(void); diff --git a/usr/dash/input.c b/usr/dash/input.c new file mode 100644 index 0000000..d31c45b --- /dev/null +++ b/usr/dash/input.c @@ -0,0 +1,532 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <stdio.h> /* defines BUFSIZ */ +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +/* + * This file implements the input routines used by the parser. + */ + +#include "eval.h" +#include "shell.h" +#include "redir.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "options.h" +#include "memalloc.h" +#include "error.h" +#include "alias.h" +#include "parser.h" +#include "main.h" +#ifndef SMALL +#include "myhistedit.h" +#endif + +#ifdef HETIO +#include "hetio.h" +#endif + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ +#define IBUFSIZ (BUFSIZ + 1) + +MKINIT +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; + struct alias *ap; /* if push was associated with an alias */ + char *string; /* remember the string since it may change */ +}; + +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ + +MKINIT +struct parsefile { + struct parsefile *prev; /* preceding file on stack */ + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in this line */ + int lleft; /* number of chars left in this buffer */ + char *nextc; /* next char in buffer */ + char *buf; /* input buffer */ + struct strpush *strpush; /* for pushing strings at this level */ + struct strpush basestrpush; /* so pushing one is fast */ +}; + + +int plinno = 1; /* input line number */ +int parsenleft; /* copy of parsefile->nleft */ +MKINIT int parselleft; /* copy of parsefile->lleft */ +char *parsenextc; /* copy of parsefile->nextc */ +MKINIT struct parsefile basepf; /* top level input file */ +MKINIT char basebuf[IBUFSIZ]; /* buffer for top level input file */ +struct parsefile *parsefile = &basepf; /* current input file */ +int whichprompt; /* 1 == PS1, 2 == PS2 */ + +#ifndef SMALL +EditLine *el; /* cookie for editline package */ +#endif + +STATIC void pushfile(void); +static int preadfd(void); +static void setinputfd(int fd, int push); + +#ifdef mkinit +INCLUDE <stdio.h> +INCLUDE "input.h" +INCLUDE "error.h" + +INIT { + basepf.nextc = basepf.buf = basebuf; +} + +RESET { + parselleft = parsenleft = 0; /* clear input buffer */ + popallfiles(); +} +#endif + + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +int +pgetc(void) +{ + return pgetc_macro(); +} + + +/* + * Same as pgetc(), but ignores PEOA. + */ + +int +pgetc2() +{ + int c; + do { + c = pgetc_macro(); + } while (c == PEOA); + return c; +} + + +static int +preadfd(void) +{ + int nr; + char *buf = parsefile->buf; + parsenextc = buf; + +retry: +#ifndef SMALL + if (parsefile->fd == 0 && el) { + static const char *rl_cp; + static int el_len; + + if (rl_cp == NULL) + rl_cp = el_gets(el, &el_len); + if (rl_cp == NULL) + nr = 0; + else { + nr = el_len; + if (nr > IBUFSIZ - 1) + nr = IBUFSIZ - 1; + memcpy(buf, rl_cp, nr); + if (nr != el_len) { + el_len -= nr; + rl_cp += nr; + } else + rl_cp = 0; + } + + } else +#endif + +#ifdef HETIO + nr = hetio_read_input(parsefile->fd); + if (nr == -255) +#endif + nr = read(parsefile->fd, buf, IBUFSIZ - 1); + + + if (nr < 0) { + if (errno == EINTR) + goto retry; + if (parsefile->fd == 0 && errno == EWOULDBLOCK) { + int flags = fcntl(0, F_GETFL, 0); + if (flags >= 0 && flags & O_NONBLOCK) { + flags &=~ O_NONBLOCK; + if (fcntl(0, F_SETFL, flags) >= 0) { + out2str("sh: turning off NDELAY mode\n"); + goto retry; + } + } + } + } + return nr; +} + +/* + * Refill the input buffer and return the next input character: + * + * 1) If a string was pushed back on the input, pop it; + * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading + * from a string so we can't refill the buffer, return EOF. + * 3) If the is more stuff in this buffer, use it else call read to fill it. + * 4) Process input up to the next newline, deleting nul characters. + */ + +int +preadbuffer(void) +{ + char *q; + int more; +#ifndef SMALL + int something; +#endif + char savec; + + while (unlikely(parsefile->strpush)) { + if ( + parsenleft == -1 && parsefile->strpush->ap && + parsenextc[-1] != ' ' && parsenextc[-1] != '\t' + ) { + return PEOA; + } + popstring(); + if (--parsenleft >= 0) + return (signed char)*parsenextc++; + } + if (unlikely(parsenleft == EOF_NLEFT || parsefile->buf == NULL)) + return PEOF; + flushout(&output); +#ifdef FLUSHERR + flushout(&errout); +#endif + + more = parselleft; + if (more <= 0) { +again: + if ((more = preadfd()) <= 0) { + parselleft = parsenleft = EOF_NLEFT; + return PEOF; + } + } + + q = parsenextc; + + /* delete nul characters */ +#ifndef SMALL + something = 0; +#endif + for (;;) { + int c; + + more--; + c = *q; + + if (!c) + memmove(q, q + 1, more); + else { + q++; + + if (c == '\n') { + parsenleft = q - parsenextc - 1; + break; + } + +#ifndef SMALL + switch (c) { + default: + something = 1; + /* fall through */ + case '\t': + case ' ': + break; + } +#endif + } + + if (more <= 0) { + parsenleft = q - parsenextc - 1; + if (parsenleft < 0) + goto again; + break; + } + } + parselleft = more; + + savec = *q; + *q = '\0'; + +#ifndef SMALL + if (parsefile->fd == 0 && hist && something) { + HistEvent he; + INTOFF; + history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, + parsenextc); + INTON; + } +#endif + + if (vflag) { + out2str(parsenextc); +#ifdef FLUSHERR + flushout(out2); +#endif + } + + *q = savec; + + return (signed char)*parsenextc++; +} + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +void +pungetc(void) +{ + parsenleft++; + parsenextc--; +} + +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +void +pushstring(char *s, void *ap) +{ + struct strpush *sp; + size_t len; + + len = strlen(s); + INTOFF; +/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ + if (parsefile->strpush) { + sp = ckmalloc(sizeof (struct strpush)); + sp->prev = parsefile->strpush; + parsefile->strpush = sp; + } else + sp = parsefile->strpush = &(parsefile->basestrpush); + sp->prevstring = parsenextc; + sp->prevnleft = parsenleft; + sp->ap = (struct alias *)ap; + if (ap) { + ((struct alias *)ap)->flag |= ALIASINUSE; + sp->string = s; + } + parsenextc = s; + parsenleft = len; + INTON; +} + +void +popstring(void) +{ + struct strpush *sp = parsefile->strpush; + + INTOFF; + if (sp->ap) { + if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { + checkkwd |= CHKALIAS; + } + if (sp->string != sp->ap->val) { + ckfree(sp->string); + } + sp->ap->flag &= ~ALIASINUSE; + if (sp->ap->flag & ALIASDEAD) { + unalias(sp->ap->name); + } + } + parsenextc = sp->prevstring; + parsenleft = sp->prevnleft; +/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ + parsefile->strpush = sp->prev; + if (sp != &(parsefile->basestrpush)) + ckfree(sp); + INTON; +} + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +int +setinputfile(const char *fname, int flags) +{ + int fd; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) { + if (flags & INPUT_NOFILE_OK) + goto out; + exitstatus = 127; + exerror(EXERROR, "Can't open %s", fname); + } + if (fd < 10) + fd = savefd(fd, fd); + setinputfd(fd, flags & INPUT_PUSH_FILE); +out: + INTON; + return fd; +} + + +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ + +static void +setinputfd(int fd, int push) +{ + if (push) { + pushfile(); + parsefile->buf = 0; + } + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(IBUFSIZ); + parselleft = parsenleft = 0; + plinno = 1; +} + + +/* + * Like setinputfile, but takes input from a string. + */ + +void +setinputstring(char *string) +{ + INTOFF; + pushfile(); + parsenextc = string; + parsenleft = strlen(string); + parsefile->buf = NULL; + plinno = 1; + INTON; +} + + + +/* + * To handle the "." command, a stack of input files is used. Pushfile + * adds a new entry to the stack and popfile restores the previous level. + */ + +STATIC void +pushfile(void) +{ + struct parsefile *pf; + + parsefile->nleft = parsenleft; + parsefile->lleft = parselleft; + parsefile->nextc = parsenextc; + parsefile->linno = plinno; + pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); + pf->prev = parsefile; + pf->fd = -1; + pf->strpush = NULL; + pf->basestrpush.prev = NULL; + parsefile = pf; +} + + +void +popfile(void) +{ + struct parsefile *pf = parsefile; + + INTOFF; + if (pf->fd >= 0) + close(pf->fd); + if (pf->buf) + ckfree(pf->buf); + while (pf->strpush) + popstring(); + parsefile = pf->prev; + ckfree(pf); + parsenleft = parsefile->nleft; + parselleft = parsefile->lleft; + parsenextc = parsefile->nextc; + plinno = parsefile->linno; + INTON; +} + + +/* + * Return to top level. + */ + +void +popallfiles(void) +{ + while (parsefile != &basepf) + popfile(); +} + + + +/* + * Close the file(s) that the shell is reading commands from. Called + * after a fork is done. + */ + +void +closescript(void) +{ + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} diff --git a/usr/dash/input.h b/usr/dash/input.h new file mode 100644 index 0000000..50a7797 --- /dev/null +++ b/usr/dash/input.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)input.h 8.2 (Berkeley) 5/4/95 + */ + +/* PEOF (the end of file marker) is defined in syntax.h */ + +enum { + INPUT_PUSH_FILE = 1, + INPUT_NOFILE_OK = 2, +}; + +/* + * The input line number. Input.c just defines this variable, and saves + * and restores it when files are pushed and popped. The user of this + * package must set its value. + */ +extern int plinno; +extern int parsenleft; /* number of characters left in input buffer */ +extern char *parsenextc; /* next character in input buffer */ + +int pgetc(void); +int pgetc2(void); +int preadbuffer(void); +void pungetc(void); +void pushstring(char *, void *); +void popstring(void); +int setinputfile(const char *, int); +void setinputstring(char *); +void popfile(void); +void popallfiles(void); +void closescript(void); + +#define pgetc_macro() \ + (--parsenleft >= 0 ? (signed char)*parsenextc++ : preadbuffer()) diff --git a/usr/dash/jobs.c b/usr/dash/jobs.c new file mode 100644 index 0000000..f027cc1 --- /dev/null +++ b/usr/dash/jobs.c @@ -0,0 +1,1504 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif +#include <sys/types.h> +#include <sys/param.h> +#ifdef BSD +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif +#include <sys/ioctl.h> + +#include "shell.h" +#if JOBS +#include <termios.h> +#undef CEOF /* syntax.h redefines this */ +#endif +#include "redir.h" +#include "show.h" +#include "main.h" +#include "parser.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "trap.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "system.h" + +/* mode flags for set_curjob */ +#define CUR_DELETE 2 +#define CUR_RUNNING 1 +#define CUR_STOPPED 0 + +/* mode flags for dowait */ +#define DOWAIT_NORMAL 0 +#define DOWAIT_BLOCK 1 +#define DOWAIT_WAITCMD 2 + +/* array of jobs */ +static struct job *jobtab; +/* size of array */ +static unsigned njobs; +/* pid of last background process */ +pid_t backgndpid; + +#if JOBS +/* pgrp of shell on invocation */ +static int initialpgrp; +/* control terminal */ +static int ttyfd = -1; +#endif + +/* current job */ +static struct job *curjob; +/* number of presumed living untracked jobs */ +static int jobless; + +STATIC void set_curjob(struct job *, unsigned); +STATIC int jobno(const struct job *); +STATIC int sprint_status(char *, int, int); +STATIC void freejob(struct job *); +STATIC struct job *getjob(const char *, int); +STATIC struct job *growjobtab(void); +STATIC void forkchild(struct job *, union node *, int); +STATIC void forkparent(struct job *, union node *, int, pid_t); +STATIC int dowait(int, struct job *); +#ifdef SYSV +STATIC int onsigchild(void); +#endif +STATIC int waitproc(int, int *); +STATIC char *commandtext(union node *); +STATIC void cmdtxt(union node *); +STATIC void cmdlist(union node *, int); +STATIC void cmdputs(const char *); +STATIC void showpipe(struct job *, struct output *); +STATIC int getstatus(struct job *); + +#if JOBS +static int restartjob(struct job *, int); +static void xtcsetpgrp(int, pid_t); +#endif + +STATIC void +set_curjob(struct job *jp, unsigned mode) +{ + struct job *jp1; + struct job **jpp, **curp; + + /* first remove from list */ + jpp = curp = &curjob; + do { + jp1 = *jpp; + if (jp1 == jp) + break; + jpp = &jp1->prev_job; + } while (1); + *jpp = jp1->prev_job; + + /* Then re-insert in correct position */ + jpp = curp; + switch (mode) { + default: +#ifdef DEBUG + abort(); +#endif + case CUR_DELETE: + /* job being deleted */ + break; + case CUR_RUNNING: + /* newly created job or backgrounded job, + put after all stopped jobs. */ + do { + jp1 = *jpp; + if (!JOBS || !jp1 || jp1->state != JOBSTOPPED) + break; + jpp = &jp1->prev_job; + } while (1); + /* FALLTHROUGH */ +#if JOBS + case CUR_STOPPED: +#endif + /* newly stopped job - becomes curjob */ + jp->prev_job = *jpp; + *jpp = jp; + break; + } +} + +#if JOBS +/* + * Turn job control on and off. + * + * Note: This code assumes that the third arg to ioctl is a character + * pointer, which is true on Berkeley systems but not System V. Since + * System V doesn't have job control yet, this isn't a problem now. + * + * Called with interrupts off. + */ + +int jobctl; + +void +setjobctl(int on) +{ + int fd; + int pgrp; + + if (on == jobctl || rootshell == 0) + return; + if (on) { + int ofd; + ofd = fd = open(_PATH_TTY, O_RDWR); + if (fd < 0) { + fd += 3; + while (!isatty(fd)) + if (--fd < 0) + goto out; + } + fd = savefd(fd, ofd); + do { /* while we are in the background */ + if ((pgrp = tcgetpgrp(fd)) < 0) { +out: + sh_warnx("can't access tty; job control turned off"); + mflag = on = 0; + goto close; + } + if (pgrp == getpgrp()) + break; + killpg(0, SIGTTIN); + } while (1); + initialpgrp = pgrp; + + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + pgrp = rootpid; + setpgid(0, pgrp); + xtcsetpgrp(fd, pgrp); + } else { + /* turning job control off */ + fd = ttyfd; + pgrp = initialpgrp; + xtcsetpgrp(fd, pgrp); + setpgid(0, pgrp); + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); +close: + close(fd); + fd = -1; + } + ttyfd = fd; + jobctl = on; +} +#endif + + +int +killcmd(argc, argv) + int argc; + char **argv; +{ + int signo = -1; + int list = 0; + int i; + pid_t pid; + struct job *jp; + + if (argc <= 1) { +usage: + sh_error( +"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n" +"kill -l [exitstatus]" + ); + } + + if (**++argv == '-') { + signo = decode_signal(*argv + 1, 1); + if (signo < 0) { + int c; + + while ((c = nextopt("ls:")) != '\0') + switch (c) { + default: +#ifdef DEBUG + abort(); +#endif + case 'l': + list = 1; + break; + case 's': + signo = decode_signal(optionarg, 1); + if (signo < 0) { + sh_error( + "invalid signal number or name: %s", + optionarg + ); + } + break; + } + argv = argptr; + } else + argv++; + } + + if (!list && signo < 0) + signo = SIGTERM; + + if ((signo < 0 || !*argv) ^ list) { + goto usage; + } + + if (list) { + struct output *out; + + out = out1; + if (!*argv) { + outstr("0\n", out); + for (i = 1; i < NSIG; i++) { + outfmt(out, snlfmt, signal_name(i)); + } + return 0; + } + signo = number(*argv); + if (signo > 128) + signo -= 128; + if (0 < signo && signo < NSIG) + outfmt(out, snlfmt, signal_name(signo)); + else + sh_error("invalid signal number or exit status: %s", + *argv); + return 0; + } + + i = 0; + do { + if (**argv == '%') { + jp = getjob(*argv, 0); + pid = -jp->ps[0].pid; + } else + pid = **argv == '-' ? + -number(*argv + 1) : number(*argv); + if (kill(pid, signo) != 0) { + sh_warnx("%s\n", strerror(errno)); + i = 1; + } + } while (*++argv); + + return i; +} + +STATIC int +jobno(const struct job *jp) +{ + return jp - jobtab + 1; +} + +#if JOBS +int +fgcmd(int argc, char **argv) +{ + struct job *jp; + struct output *out; + int mode; + int retval; + + mode = (**argv == 'f') ? FORK_FG : FORK_BG; + nextopt(nullstr); + argv = argptr; + out = out1; + do { + jp = getjob(*argv, 1); + if (mode == FORK_BG) { + set_curjob(jp, CUR_RUNNING); + outfmt(out, "[%d] ", jobno(jp)); + } + outstr(jp->ps->cmd, out); + showpipe(jp, out); + retval = restartjob(jp, mode); + } while (*argv && *++argv); + return retval; +} + +int bgcmd(int argc, char **argv) +#ifdef HAVE_ALIAS_ATTRIBUTE + __attribute__((__alias__("fgcmd"))); +#else +{ + return fgcmd(argc, argv); +} +#endif + + +STATIC int +restartjob(struct job *jp, int mode) +{ + struct procstat *ps; + int i; + int status; + pid_t pgid; + + INTOFF; + if (jp->state == JOBDONE) + goto out; + jp->state = JOBRUNNING; + pgid = jp->ps->pid; + if (mode == FORK_FG) + xtcsetpgrp(ttyfd, pgid); + killpg(pgid, SIGCONT); + ps = jp->ps; + i = jp->nprocs; + do { + if (WIFSTOPPED(ps->status)) { + ps->status = -1; + } + } while (ps++, --i); +out: + status = (mode == FORK_FG) ? waitforjob(jp) : 0; + INTON; + return status; +} +#endif + +STATIC int +sprint_status(char *s, int status, int sigonly) +{ + int col; + int st; + + col = 0; + st = WEXITSTATUS(status); + if (!WIFEXITED(status)) { +#if JOBS + st = WSTOPSIG(status); + if (!WIFSTOPPED(status)) +#endif + st = WTERMSIG(status); + if (sigonly) { + if (st == SIGINT || st == SIGPIPE) + goto out; +#if JOBS + if (WIFSTOPPED(status)) + goto out; +#endif + } + col = fmtstr(s, 32, "%s", strsignal(st)); +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + col += fmtstr(s + col, 16, " (core dumped)"); + } +#endif + } else if (!sigonly) { + if (st) + col = fmtstr(s, 16, "Done(%d)", st); + else + col = fmtstr(s, 16, "Done"); + } + +out: + return col; +} + +static void +showjob(struct output *out, struct job *jp, int mode) +{ + struct procstat *ps; + struct procstat *psend; + int col; + int indent; + char s[80]; + + ps = jp->ps; + + if (mode & SHOW_PGID) { + /* just output process (group) id of pipeline */ + outfmt(out, "%d\n", ps->pid); + return; + } + + col = fmtstr(s, 16, "[%d] ", jobno(jp)); + indent = col; + + if (jp == curjob) + s[col - 2] = '+'; + else if (curjob && jp == curjob->prev_job) + s[col - 2] = '-'; + + if (mode & SHOW_PID) + col += fmtstr(s + col, 16, "%d ", ps->pid); + + psend = ps + jp->nprocs; + + if (jp->state == JOBRUNNING) { + scopy("Running", s + col); + col += strlen("Running"); + } else { + int status = psend[-1].status; +#if JOBS + if (jp->state == JOBSTOPPED) + status = jp->stopstatus; +#endif + col += sprint_status(s + col, status, 0); + } + + goto start; + + do { + /* for each process */ + col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3; + +start: + outfmt( + out, "%s%*c%s", + s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd + ); + if (!(mode & SHOW_PID)) { + showpipe(jp, out); + break; + } + if (++ps == psend) { + outcslow('\n', out); + break; + } + } while (1); + + jp->changed = 0; + + if (jp->state == JOBDONE) { + TRACE(("showjob: freeing job %d\n", jobno(jp))); + freejob(jp); + } +} + + +int +jobscmd(int argc, char **argv) +{ + int mode, m; + struct output *out; + + mode = 0; + while ((m = nextopt("lp"))) + if (m == 'l') + mode = SHOW_PID; + else + mode = SHOW_PGID; + + out = out1; + argv = argptr; + if (*argv) + do + showjob(out, getjob(*argv,0), mode); + while (*++argv); + else + showjobs(out, mode); + + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + */ + +void +showjobs(struct output *out, int mode) +{ + struct job *jp; + + TRACE(("showjobs(%x) called\n", mode)); + + /* If not even one one job changed, there is nothing to do */ + while (dowait(DOWAIT_NORMAL, NULL) > 0) + continue; + + for (jp = curjob; jp; jp = jp->prev_job) { + if (!(mode & SHOW_CHANGED) || jp->changed) + showjob(out, jp, mode); + } +} + +/* + * Mark a job structure as unused. + */ + +STATIC void +freejob(struct job *jp) +{ + struct procstat *ps; + int i; + + INTOFF; + for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { + if (ps->cmd != nullstr) + ckfree(ps->cmd); + } + if (jp->ps != &jp->ps0) + ckfree(jp->ps); + jp->used = 0; + set_curjob(jp, CUR_DELETE); + INTON; +} + + + +int +waitcmd(int argc, char **argv) +{ + struct job *job; + int retval; + struct job *jp; + + nextopt(nullstr); + retval = 0; + + argv = argptr; + if (!*argv) { + /* wait for all jobs */ + for (;;) { + jp = curjob; + while (1) { + if (!jp) { + /* no running procs */ + goto out; + } + if (jp->state == JOBRUNNING) + break; + jp->waited = 1; + jp = jp->prev_job; + } + if (dowait(DOWAIT_WAITCMD, 0) <= 0) + goto sigout; + } + } + + retval = 127; + do { + if (**argv != '%') { + pid_t pid = number(*argv); + job = curjob; + goto start; + do { + if (job->ps[job->nprocs - 1].pid == pid) + break; + job = job->prev_job; +start: + if (!job) + goto repeat; + } while (1); + } else + job = getjob(*argv, 0); + /* loop until process terminated or stopped */ + while (job->state == JOBRUNNING) + if (dowait(DOWAIT_WAITCMD, 0) <= 0) + goto sigout; + job->waited = 1; + retval = getstatus(job); +repeat: + ; + } while (*++argv); + +out: + return retval; + +sigout: + retval = 128 + pendingsigs; + goto out; +} + + + +/* + * Convert a job name to a job structure. + */ + +STATIC struct job * +getjob(const char *name, int getctl) +{ + struct job *jp; + struct job *found; + const char *err_msg = "No such job: %s"; + unsigned num; + int c; + const char *p; + char *(*match)(const char *, const char *); + + jp = curjob; + p = name; + if (!p) + goto currentjob; + + if (*p != '%') + goto err; + + c = *++p; + if (!c) + goto currentjob; + + if (!p[1]) { + if (c == '+' || c == '%') { +currentjob: + err_msg = "No current job"; + goto check; + } else if (c == '-') { + if (jp) + jp = jp->prev_job; + err_msg = "No previous job"; +check: + if (!jp) + goto err; + goto gotit; + } + } + + if (is_number(p)) { + num = atoi(p); + if (num < njobs) { + jp = jobtab + num - 1; + if (jp->used) + goto gotit; + goto err; + } + } + + match = prefix; + if (*p == '?') { + match = strstr; + p++; + } + + found = 0; + while (1) { + if (!jp) + goto err; + if (match(jp->ps[0].cmd, p)) { + if (found) + goto err; + found = jp; + err_msg = "%s: ambiguous"; + } + jp = jp->prev_job; + } + +gotit: +#if JOBS + err_msg = "job %s not created under job control"; + if (getctl && jp->jobctl == 0) + goto err; +#endif + return jp; +err: + sh_error(err_msg, name); +} + + + +/* + * Return a new job structure. + * Called with interrupts off. + */ + +struct job * +makejob(union node *node, int nprocs) +{ + int i; + struct job *jp; + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + jp = growjobtab(); + break; + } + if (jp->used == 0) + break; + if (jp->state != JOBDONE || !jp->waited) + continue; + if (jobctl) + continue; + freejob(jp); + break; + } + memset(jp, 0, sizeof(*jp)); +#if JOBS + if (jobctl) + jp->jobctl = 1; +#endif + jp->prev_job = curjob; + curjob = jp; + jp->used = 1; + jp->ps = &jp->ps0; + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } + TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, + jobno(jp))); + return jp; +} + +STATIC struct job * +growjobtab(void) +{ + size_t len; + ptrdiff_t offset; + struct job *jp, *jq; + + len = njobs * sizeof(*jp); + jq = jobtab; + jp = ckrealloc(jq, len + 4 * sizeof(*jp)); + + offset = (char *)jp - (char *)jq; + if (offset) { + /* Relocate pointers */ + size_t l = len; + + jq = (struct job *)((char *)jq + l); + while (l) { + l -= sizeof(*jp); + jq--; +#define joff(p) ((struct job *)((char *)(p) + l)) +#define jmove(p) (p) = (void *)((char *)(p) + offset) + if (likely(joff(jp)->ps == &jq->ps0)) + jmove(joff(jp)->ps); + if (joff(jp)->prev_job) + jmove(joff(jp)->prev_job); + } + if (curjob) + jmove(curjob); +#undef joff +#undef jmove + } + + njobs += 4; + jobtab = jp; + jp = (struct job *)((char *)jp + len); + jq = jp + 3; + do { + jq->used = 0; + } while (--jq >= jp); + return jp; +} + + +/* + * Fork off a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + * + * Called with interrupts off. + */ + +STATIC inline void +forkchild(struct job *jp, union node *n, int mode) +{ + int oldlvl; + + TRACE(("Child shell %d\n", getpid())); + oldlvl = shlvl; + shlvl++; + + closescript(); + clear_traps(); +#if JOBS + /* do job control only in root shell */ + jobctl = 0; + if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { + pid_t pgrp; + + if (jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + /* This can fail because we are doing it in the parent also */ + (void)setpgid(0, pgrp); + if (mode == FORK_FG) + xtcsetpgrp(ttyfd, pgrp); + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } else +#endif + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if (jp->nprocs == 0) { + close(0); + if (open(_PATH_DEVNULL, O_RDONLY) != 0) + sh_error("Can't open %s", _PATH_DEVNULL); + } + } + if (!oldlvl && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + for (jp = curjob; jp; jp = jp->prev_job) + freejob(jp); + jobless = 0; +} + +STATIC inline void +forkparent(struct job *jp, union node *n, int mode, pid_t pid) +{ + TRACE(("In parent shell: child = %d\n", pid)); + if (!jp) { + while (jobless && dowait(DOWAIT_NORMAL, 0) > 0); + jobless++; + return; + } +#if JOBS + if (mode != FORK_NOJOB && jp->jobctl) { + int pgrp; + + if (jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; + /* This can fail because we are doing it in the child also */ + (void)setpgid(pid, pgrp); + } +#endif + if (mode == FORK_BG) { + backgndpid = pid; /* set $! */ + set_curjob(jp, CUR_RUNNING); + } + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; + if (jobctl && n) + ps->cmd = commandtext(n); + } +} + +int +forkshell(struct job *jp, union node *n, int mode) +{ + int pid; + + TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode)); + pid = fork(); + if (pid < 0) { + TRACE(("Fork failed, errno=%d", errno)); + if (jp) + freejob(jp); + sh_error("Cannot fork"); + } + if (pid == 0) + forkchild(jp, n, mode); + else + forkparent(jp, n, mode, pid); + return pid; +} + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * forground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + * + * Called with interrupts off. + */ + +int +waitforjob(struct job *jp) +{ + int st; + + TRACE(("waitforjob(%%%d) called\n", jobno(jp))); + while (jp->state == JOBRUNNING) { + dowait(DOWAIT_BLOCK, jp); + } + st = getstatus(jp); +#if JOBS + if (jp->jobctl) { + xtcsetpgrp(ttyfd, rootpid); + /* + * This is truly gross. + * If we're doing job control, then we did a TIOCSPGRP which + * caused us (the shell) to no longer be in the controlling + * session -- so we wouldn't have seen any ^C/SIGINT. So, we + * intuit from the subprocess exit status whether a SIGINT + * occurred, and if so interrupt ourselves. Yuck. - mycroft + */ + if (jp->sigint) + raise(SIGINT); + } +#endif + if (! JOBS || jp->state == JOBDONE) + freejob(jp); + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +STATIC int +dowait(int block, struct job *job) +{ + int pid; + int status; + struct job *jp; + struct job *thisjob = NULL; + int state; + + INTOFF; + TRACE(("dowait(%d) called\n", block)); + pid = waitproc(block, &status); + TRACE(("wait returns pid %d, status=%d\n", pid, status)); + if (pid <= 0) + goto out; + + for (jp = curjob; jp; jp = jp->prev_job) { + struct procstat *sp; + struct procstat *spend; + if (jp->state == JOBDONE) + continue; + state = JOBDONE; + spend = jp->ps + jp->nprocs; + sp = jp->ps; + do { + if (sp->pid == pid) { + TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + state = JOBRUNNING; +#if JOBS + if (state == JOBRUNNING) + continue; + if (WIFSTOPPED(sp->status)) { + jp->stopstatus = sp->status; + state = JOBSTOPPED; + } +#endif + } while (++sp < spend); + if (thisjob) + goto gotjob; + } + if (!JOBS || !WIFSTOPPED(status)) + jobless--; + goto out; + +gotjob: + if (state != JOBRUNNING) { + thisjob->changed = 1; + + if (thisjob->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state)); + thisjob->state = state; +#if JOBS + if (state == JOBSTOPPED) { + set_curjob(thisjob, CUR_STOPPED); + } +#endif + } + } + +out: + INTON; + + if (thisjob && thisjob == job) { + char s[48 + 1]; + int len; + + len = sprint_status(s, status, 1); + if (len) { + s[len] = '\n'; + s[len + 1] = 0; + outstr(s, out2); + } + } + return pid; +} + + + +/* + * Do a wait system call. If job control is compiled in, we accept + * stopped processes. If block is zero, we return a value of zero + * rather than blocking. + * + * System V doesn't have a non-blocking wait system call. It does + * have a SIGCLD signal that is sent to a process when one of it's + * children dies. The obvious way to use SIGCLD would be to install + * a handler for SIGCLD which simply bumped a counter when a SIGCLD + * was received, and have waitproc bump another counter when it got + * the status of a process. Waitproc would then know that a wait + * system call would not block if the two counters were different. + * This approach doesn't work because if a process has children that + * have not been waited for, System V will send it a SIGCLD when it + * installs a signal handler for SIGCLD. What this means is that when + * a child exits, the shell will be sent SIGCLD signals continuously + * until is runs out of stack space, unless it does a wait call before + * restoring the signal handler. The code below takes advantage of + * this (mis)feature by installing a signal handler for SIGCLD and + * then checking to see whether it was called. If there are any + * children to be waited for, it will be. + * + * If neither SYSV nor BSD is defined, we don't implement nonblocking + * waits at all. In this case, the user will not be informed when + * a background process until the next time she runs a real program + * (as opposed to running a builtin command or just typing return), + * and the jobs command may give out of date information. + */ + +#ifdef SYSV +STATIC int gotsigchild; + +STATIC int onsigchild() { + gotsigchild = 1; +} +#endif + + +STATIC int +waitproc(int block, int *status) +{ + sigset_t mask, oldmask; + int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; + int err; + +#if JOBS + if (jobctl) + flags |= WUNTRACED; +#endif + + do { + gotsigchld = 0; + err = wait3(status, flags, NULL); + if (err || !block) + break; + + block = 0; + + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, &oldmask); + + while (!gotsigchld && !pendingsigs) + sigsuspend(&oldmask); + + sigclearmask(); + } while (gotsigchld); + + return err; +} + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ +int job_warning; +int +stoppedjobs(void) +{ + struct job *jp; + int retval; + + retval = 0; + if (job_warning) + goto out; + jp = curjob; + if (jp && jp->state == JOBSTOPPED) { + out2str("You have stopped jobs.\n"); + job_warning = 2; + retval++; + } + +out: + return retval; +} + +/* + * Return a string identifying a command (to be printed by the + * jobs command). + */ + +STATIC char *cmdnextc; + +STATIC char * +commandtext(union node *n) +{ + char *name; + + STARTSTACKSTR(cmdnextc); + cmdtxt(n); + name = stackblock(); + TRACE(("commandtext: name %p, end %p\n", name, cmdnextc)); + return savestr(name); +} + + +STATIC void +cmdtxt(union node *n) +{ + union node *np; + struct nodelist *lp; + const char *p; + char s[2]; + + if (!n) + return; + switch (n->type) { + default: +#if DEBUG + abort(); +#endif + case NPIPE: + lp = n->npipe.cmdlist; + for (;;) { + cmdtxt(lp->n); + lp = lp->next; + if (!lp) + break; + cmdputs(" | "); + } + break; + case NSEMI: + p = "; "; + goto binop; + case NAND: + p = " && "; + goto binop; + case NOR: + p = " || "; +binop: + cmdtxt(n->nbinary.ch1); + cmdputs(p); + n = n->nbinary.ch2; + goto donode; + case NREDIR: + case NBACKGND: + n = n->nredir.n; + goto donode; + case NNOT: + cmdputs("!"); + n = n->nnot.com; +donode: + cmdtxt(n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + if (n->nif.elsepart) { + cmdtxt(n->nif.ifpart); + cmdputs("; else "); + n = n->nif.elsepart; + } else { + n = n->nif.ifpart; + } + p = "; fi"; + goto dotail; + case NSUBSHELL: + cmdputs("("); + n = n->nredir.n; + p = ")"; + goto dotail; + case NWHILE: + p = "while "; + goto until; + case NUNTIL: + p = "until "; +until: + cmdputs(p); + cmdtxt(n->nbinary.ch1); + n = n->nbinary.ch2; + p = "; done"; +dodo: + cmdputs("; do "); +dotail: + cmdtxt(n); + goto dotail2; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in "); + cmdlist(n->nfor.args, 1); + n = n->nfor.body; + p = "; done"; + goto dodo; + case NDEFUN: + cmdputs(n->ndefun.text); + p = "() { ... }"; + goto dotail2; + case NCMD: + cmdlist(n->ncmd.args, 1); + cmdlist(n->ncmd.redirect, 0); + break; + case NARG: + p = n->narg.text; +dotail2: + cmdputs(p); + break; + case NHERE: + case NXHERE: + p = "<<..."; + goto dotail2; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in "); + for (np = n->ncase.cases; np; np = np->nclist.next) { + cmdtxt(np->nclist.pattern); + cmdputs(") "); + cmdtxt(np->nclist.body); + cmdputs(";; "); + } + p = "esac"; + goto dotail2; + case NTO: + p = ">"; + goto redir; + case NCLOBBER: + p = ">|"; + goto redir; + case NAPPEND: + p = ">>"; + goto redir; + case NTOFD: + p = ">&"; + goto redir; + case NFROM: + p = "<"; + goto redir; + case NFROMFD: + p = "<&"; + goto redir; + case NFROMTO: + p = "<>"; +redir: + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + p = s; + goto dotail2; + } else { + n = n->nfile.fname; + goto donode; + } + } +} + +STATIC void +cmdlist(union node *np, int sep) +{ + for (; np; np = np->narg.next) { + if (!sep) + cmdputs(spcstr); + cmdtxt(np); + if (sep && np->narg.next) + cmdputs(spcstr); + } +} + + +STATIC void +cmdputs(const char *s) +{ + const char *p, *str; + char cc[2] = " "; + char *nextc; + signed char c; + int subtype = 0; + int quoted = 0; + static const char vstype[VSTYPE + 1][4] = { + "", "}", "-", "+", "?", "=", + "%", "%%", "#", "##", + }; + + nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); + p = s; + while ((c = *p++) != 0) { + str = 0; + switch (c) { + case CTLESC: + c = *p++; + break; + case CTLVAR: + subtype = *p++; + if ((subtype & VSTYPE) == VSLENGTH) + str = "${#"; + else + str = "${"; + goto dostr; + case CTLENDVAR: + str = "\"}"; + str += !(quoted & 1); + quoted >>= 1; + subtype = 0; + goto dostr; + case CTLBACKQ: + str = "$(...)"; + goto dostr; + case CTLARI: + str = "$(("; + goto dostr; + case CTLENDARI: + str = "))"; + goto dostr; + case CTLQUOTEMARK: + quoted ^= 1; + c = '"'; + break; + case '=': + if (subtype == 0) + break; + if ((subtype & VSTYPE) != VSNORMAL) + quoted <<= 1; + str = vstype[subtype & VSTYPE]; + if (subtype & VSNUL) + c = ':'; + else + goto checkstr; + break; + case '\'': + case '\\': + case '"': + case '$': + /* These can only happen inside quotes */ + cc[0] = c; + str = cc; + c = '\\'; + break; + default: + break; + } + USTPUTC(c, nextc); +checkstr: + if (!str) + continue; +dostr: + while ((c = *str++)) { + USTPUTC(c, nextc); + } + } + if (quoted & 1) { + USTPUTC('"', nextc); + } + *nextc = 0; + cmdnextc = nextc; +} + + +STATIC void +showpipe(struct job *jp, struct output *out) +{ + struct procstat *sp; + struct procstat *spend; + + spend = jp->ps + jp->nprocs; + for (sp = jp->ps + 1; sp < spend; sp++) + outfmt(out, " | %s", sp->cmd); + outcslow('\n', out); + flushall(); +} + + +#if JOBS +STATIC void +xtcsetpgrp(int fd, pid_t pgrp) +{ + if (tcsetpgrp(fd, pgrp)) + sh_error("Cannot set tty process group (%s)", strerror(errno)); +} +#endif + + +STATIC int +getstatus(struct job *job) { + int status; + int retval; + + status = job->ps[job->nprocs - 1].status; + retval = WEXITSTATUS(status); + if (!WIFEXITED(status)) { +#if JOBS + retval = WSTOPSIG(status); + if (!WIFSTOPPED(status)) +#endif + { + /* XXX: limits number of signals */ + retval = WTERMSIG(status); +#if JOBS + if (retval == SIGINT) + job->sigint = 1; +#endif + } + retval += 128; + } + TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", + jobno(job), job->nprocs, status, retval)); + return retval; +} diff --git a/usr/dash/jobs.h b/usr/dash/jobs.h new file mode 100644 index 0000000..953ee87 --- /dev/null +++ b/usr/dash/jobs.h @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)jobs.h 8.2 (Berkeley) 5/4/95 + */ + +#include <inttypes.h> +#include <sys/types.h> + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + +/* mode flags for showjob(s) */ +#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ +#define SHOW_PID 0x04 /* include process pid */ +#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ + + +/* + * A job structure contains information about a job. A job is either a + * single process or a set of processes contained in a pipeline. In the + * latter case, pidlist will be non-NULL, and will point to a -1 terminated + * array of pids. + */ + +struct procstat { + pid_t pid; /* process id */ + int status; /* last process status from wait() */ + char *cmd; /* text of command being run */ +}; + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ +#if JOBS + int stopstatus; /* status of a stopped job */ +#endif + uint32_t + nprocs: 16, /* number of processes */ + state: 8, +#define JOBRUNNING 0 /* at least one proc running */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ +#if JOBS + sigint: 1, /* job was killed by SIGINT */ + jobctl: 1, /* job running under job control */ +#endif + waited: 1, /* true if this entry has been waited for */ + used: 1, /* true if this entry is in used */ + changed: 1; /* true if status has changed */ + struct job *prev_job; /* previous job */ +}; + +extern pid_t backgndpid; /* pid of last background process */ +extern int job_warning; /* user was warned about stopped jobs */ +#if JOBS +extern int jobctl; /* true if doing job control */ +#else +#define jobctl 0 +#endif + +void setjobctl(int); +int killcmd(int, char **); +int fgcmd(int, char **); +int bgcmd(int, char **); +int jobscmd(int, char **); +struct output; +void showjobs(struct output *, int); +int waitcmd(int, char **); +struct job *makejob(union node *, int); +int forkshell(struct job *, union node *, int); +int waitforjob(struct job *); +int stoppedjobs(void); + +#if ! JOBS +#define setjobctl(on) ((void)(on)) /* do nothing */ +#endif diff --git a/usr/dash/machdep.h b/usr/dash/machdep.h new file mode 100644 index 0000000..b1ca07a --- /dev/null +++ b/usr/dash/machdep.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)machdep.h 8.2 (Berkeley) 5/4/95 + */ + +#include "config.h" + +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ +#ifdef HAVE_STRTOD +# define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1) +#else +# define SHELL_SIZE (sizeof(union {int i; char *cp;}) - 1) +#endif + +/* + * It appears that grabstackstr() will barf with such alignments + * because stalloc() will return a string allocated in a new stackblock. + */ +#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) diff --git a/usr/dash/mail.c b/usr/dash/mail.c new file mode 100644 index 0000000..02e07f7 --- /dev/null +++ b/usr/dash/mail.c @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +/* + * Routines to check for mail. (Perhaps make part of main.c?) + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> + +#include "shell.h" +#include "nodes.h" +#include "exec.h" /* defines padvance() */ +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mail.h" +#include "mystring.h" + + +#define MAXMBOXES 10 + +/* times of mailboxes */ +static time_t mailtime[MAXMBOXES]; +/* Set if MAIL or MAILPATH is changed. */ +static int changed; + + + +/* + * Print appropriate message(s) if mail has arrived. If changed is set, + * then the value of MAIL has changed, so we just update the values. + */ + +void +chkmail(void) +{ + const char *mpath; + char *p; + char *q; + time_t *mtp; + struct stackmark smark; + struct stat64 statb; + + setstackmark(&smark); + mpath = mpathset() ? mpathval() : mailval(); + for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { + p = padvance(&mpath, nullstr); + if (p == NULL) + break; + if (*p == '\0') + continue; + for (q = p ; *q ; q++); +#ifdef DEBUG + if (q[-1] != '/') + abort(); +#endif + q[-1] = '\0'; /* delete trailing '/' */ + if (stat64(p, &statb) < 0) { + *mtp = 0; + continue; + } + if (!changed && statb.st_mtime != *mtp) { + outfmt( + &errout, snlfmt, + pathopt ? pathopt : "you have mail" + ); + } + *mtp = statb.st_mtime; + } + changed = 0; + popstackmark(&smark); +} + + +void +changemail(const char *val) +{ + changed++; +} diff --git a/usr/dash/mail.h b/usr/dash/mail.h new file mode 100644 index 0000000..3c6b21d --- /dev/null +++ b/usr/dash/mail.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)mail.h 8.2 (Berkeley) 5/4/95 + */ + +void chkmail(void); +void changemail(const char *); diff --git a/usr/dash/main.c b/usr/dash/main.c new file mode 100644 index 0000000..7df3c44 --- /dev/null +++ b/usr/dash/main.c @@ -0,0 +1,346 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <stdio.h> +#include <signal.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + + +#include "shell.h" +#include "main.h" +#include "mail.h" +#include "options.h" +#include "output.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "trap.h" +#include "var.h" +#include "show.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" +#include "exec.h" +#include "cd.h" + +#ifdef HETIO +#include "hetio.h" +#endif + +#define PROFILE 0 + +int rootpid; +int shlvl; +#ifdef __GLIBC__ +int *dash_errno; +#endif +#if PROFILE +short profile_buf[16384]; +extern int etext(); +#endif + +STATIC void read_profile(const char *); +STATIC char *find_dot_file(char *); +static int cmdloop(int); +int main(int, char **); + +/* + * Main routine. We initialize things, parse the arguments, execute + * profiles if we're a login shell, and then call cmdloop to execute + * commands. The setjmp call sets up the location to jump to when an + * exception occurs. When an exception occurs the variable "state" + * is used to figure out how far we had gotten. + */ + +int +main(int argc, char **argv) +{ + char *shinit; + volatile int state; + struct jmploc jmploc; + struct stackmark smark; + int login; + +#ifdef __GLIBC__ + dash_errno = __errno_location(); +#endif + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif + state = 0; + if (unlikely(setjmp(jmploc.loc))) { + int e; + int s; + + reset(); + + e = exception; + + s = state; + if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) + exitshell(); + + if (e == EXINT +#if ATTY + && (! attyset() || equal(termval(), "emacs")) +#endif + ) { + out2c('\n'); +#ifdef FLUSHERR + flushout(out2); +#endif + } + popstackmark(&smark); + FORCEINTON; /* enable interrupts */ + if (s == 1) + goto state1; + else if (s == 2) + goto state2; + else if (s == 3) + goto state3; + else + goto state4; + } + handler = &jmploc; +#ifdef DEBUG + opentrace(); + trputs("Shell args: "); trargs(argv); +#endif + rootpid = getpid(); + init(); + setstackmark(&smark); + login = procargs(argc, argv); + if (login) { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + read_profile("$HOME/.profile"); + } +state2: + state = 3; + if ( +#ifndef linux + getuid() == geteuid() && getgid() == getegid() && +#endif + iflag + ) { + if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { + read_profile(shinit); + } + } + popstackmark(&smark); +state3: + state = 4; + if (minusc) + evalstring(minusc, 0); + + if (sflag || minusc == NULL) { +state4: /* XXX ??? - why isn't this before the "if" statement */ + cmdloop(1); + } +#if PROFILE + monitor(0); +#endif +#if GPROF + { + extern void _mcleanup(void); + _mcleanup(); + } +#endif + exitshell(); + /* NOTREACHED */ +} + + +/* + * Read and execute commands. "Top" is nonzero for the top level command + * loop; it turns on prompting if the shell is interactive. + */ + +static int +cmdloop(int top) +{ + union node *n; + struct stackmark smark; + int inter; + int status = 0; + int numeof = 0; + + TRACE(("cmdloop(%d) called\n", top)); +#ifdef HETIO + if(iflag && top) + hetio_init(); +#endif + for (;;) { + int skip; + + setstackmark(&smark); + if (jobctl) + showjobs(out2, SHOW_CHANGED); + inter = 0; + if (iflag && top) { + inter++; + chkmail(); + } + n = parsecmd(inter); + /* showtree(n); DEBUG */ + if (n == NEOF) { + if (!top || numeof >= 50) + break; + if (!stoppedjobs()) { + if (!Iflag) + break; + out2str("\nUse \"exit\" to leave shell.\n"); + } + numeof++; + } else if (nflag == 0) { + job_warning = (job_warning == 2) ? 1 : 0; + numeof = 0; + evaltree(n, 0); + status = exitstatus; + } + popstackmark(&smark); + + skip = evalskip; + if (skip) { + evalskip &= ~SKIPFUNC; + break; + } + } + + return status; +} + + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +STATIC void +read_profile(const char *name) +{ + name = expandstr(name); + if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) + return; + + cmdloop(0); + popfile(); +} + + + +/* + * Read a file containing shell functions. + */ + +void +readcmdfile(char *name) +{ + setinputfile(name, INPUT_PUSH_FILE); + cmdloop(0); + popfile(); +} + + + +/* + * Take commands from a file. To be compatible we should do a path + * search for the file, which is necessary to find sub-commands. + */ + + +STATIC char * +find_dot_file(char *basename) +{ + char *fullname; + const char *path = pathval(); + struct stat statb; + + /* don't try this for absolute or relative paths */ + if (strchr(basename, '/')) + return basename; + + while ((fullname = padvance(&path, basename)) != NULL) { + if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { + /* + * Don't bother freeing here, since it will + * be freed by the caller. + */ + return fullname; + } + stunalloc(fullname); + } + + /* not found in the PATH */ + sh_error("%s: not found", basename); + /* NOTREACHED */ +} + +int +dotcmd(int argc, char **argv) +{ + int status = 0; + + if (argc >= 2) { /* That's what SVR2 does */ + char *fullname; + + fullname = find_dot_file(argv[1]); + setinputfile(fullname, INPUT_PUSH_FILE); + commandname = fullname; + status = cmdloop(0); + popfile(); + } + return status; +} + + +int +exitcmd(int argc, char **argv) +{ + if (stoppedjobs()) + return 0; + if (argc > 1) + exitstatus = number(argv[1]); + exraise(EXEXIT); + /* NOTREACHED */ +} diff --git a/usr/dash/main.h b/usr/dash/main.h new file mode 100644 index 0000000..19e4983 --- /dev/null +++ b/usr/dash/main.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)main.h 8.2 (Berkeley) 5/4/95 + */ + +#include <errno.h> + +/* pid of main shell */ +extern int rootpid; +/* shell level: 0 for the main shell, 1 for its children, and so on */ +extern int shlvl; +#define rootshell (!shlvl) + +#ifdef __GLIBC__ +/* glibc sucks */ +extern int *dash_errno; +#undef errno +#define errno (*dash_errno) +#endif + +void readcmdfile(char *); +int dotcmd(int, char **); +int exitcmd(int, char **); diff --git a/usr/dash/memalloc.c b/usr/dash/memalloc.c new file mode 100644 index 0000000..d8e4413 --- /dev/null +++ b/usr/dash/memalloc.c @@ -0,0 +1,302 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <stdlib.h> +#include <unistd.h> + +#include "shell.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "machdep.h" +#include "mystring.h" +#include "system.h" + +/* + * Like malloc, but returns an error when out of space. + */ + +pointer +ckmalloc(size_t nbytes) +{ + pointer p; + + p = malloc(nbytes); + if (p == NULL) + sh_error("Out of space"); + return p; +} + + +/* + * Same for realloc. + */ + +pointer +ckrealloc(pointer p, size_t nbytes) +{ + p = realloc(p, nbytes); + if (p == NULL) + sh_error("Out of space"); + return p; +} + + +/* + * Make a copy of a string in safe storage. + */ + +char * +savestr(const char *s) +{ + char *p = strdup(s); + if (!p) + sh_error("Out of space"); + return p; +} + + +/* + * Parse trees for commands are allocated in lifo order, so we use a stack + * to make this more efficient, and also to avoid all sorts of exception + * handling code to handle interrupts in the middle of a parse. + * + * The size 504 was chosen because the Ultrix malloc handles that size + * well. + */ + +/* minimum size of a block */ +#define MINSIZE SHELL_ALIGN(504) + +struct stack_block { + struct stack_block *prev; + char space[MINSIZE]; +}; + +struct stack_block stackbase; +struct stack_block *stackp = &stackbase; +char *stacknxt = stackbase.space; +size_t stacknleft = MINSIZE; +char *sstrend = stackbase.space + MINSIZE; + +pointer +stalloc(size_t nbytes) +{ + char *p; + size_t aligned; + + aligned = SHELL_ALIGN(nbytes); + if (aligned > stacknleft) { + size_t len; + size_t blocksize; + struct stack_block *sp; + + blocksize = aligned; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + len = sizeof(struct stack_block) - MINSIZE + blocksize; + if (len < blocksize) + sh_error("Out of space"); + INTOFF; + sp = ckmalloc(len); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + sstrend = stacknxt + blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += aligned; + stacknleft -= aligned; + return p; +} + + +void +stunalloc(pointer p) +{ +#ifdef DEBUG + if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) { + write(2, "stunalloc\n", 10); + abort(); + } +#endif + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + + +void pushstackmark(struct stackmark *mark, size_t len) +{ + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; + grabstackblock(len); +} + +void setstackmark(struct stackmark *mark) +{ + pushstackmark(mark, stacknxt == stackp->space && stackp != &stackbase); +} + + +void +popstackmark(struct stackmark *mark) +{ + struct stack_block *sp; + + INTOFF; + while (stackp != mark->stackp) { + sp = stackp; + stackp = sp->prev; + ckfree(sp); + } + stacknxt = mark->stacknxt; + stacknleft = mark->stacknleft; + sstrend = mark->stacknxt + mark->stacknleft; + INTON; +} + + +/* + * When the parser reads in a string, it wants to stick the string on the + * stack and only adjust the stack pointer when it knows how big the + * string is. Stackblock (defined in stack.h) returns a pointer to a block + * of space on top of the stack and stackblocklen returns the length of + * this block. Growstackblock will grow this space by at least one byte, + * possibly moving it (like realloc). Grabstackblock actually allocates the + * part of the block that has been used. + */ + +void +growstackblock(void) +{ + size_t newlen; + + newlen = stacknleft * 2; + if (newlen < stacknleft) + sh_error("Out of space"); + if (newlen < 128) + newlen += 128; + + if (stacknxt == stackp->space && stackp != &stackbase) { + struct stack_block *sp; + struct stack_block *prevstackp; + size_t grosslen; + + INTOFF; + sp = stackp; + prevstackp = sp->prev; + grosslen = newlen + sizeof(struct stack_block) - MINSIZE; + sp = ckrealloc((pointer)sp, grosslen); + sp->prev = prevstackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + sstrend = sp->space + newlen; + INTON; + } else { + char *oldspace = stacknxt; + int oldlen = stacknleft; + char *p = stalloc(newlen); + + /* free the space we just allocated */ + stacknxt = memcpy(p, oldspace, oldlen); + stacknleft += newlen; + } +} + +/* + * The following routines are somewhat easier to use than the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + +void * +growstackstr(void) +{ + size_t len = stackblocksize(); + growstackblock(); + return stackblock() + len; +} + +/* + * Called from CHECKSTRSPACE. + */ + +char * +makestrspace(size_t newlen, char *p) +{ + size_t len = p - stacknxt; + size_t size; + + for (;;) { + size_t nleft; + + size = stackblocksize(); + nleft = size - len; + if (nleft >= newlen) + break; + growstackblock(); + } + return stackblock() + len; +} + +char * +stnputs(const char *s, size_t n, char *p) +{ + p = makestrspace(n, p); + p = mempcpy(p, s, n); + return p; +} + +char * +stputs(const char *s, char *p) +{ + return stnputs(s, strlen(s), p); +} diff --git a/usr/dash/memalloc.h b/usr/dash/memalloc.h new file mode 100644 index 0000000..4b5be46 --- /dev/null +++ b/usr/dash/memalloc.h @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)memalloc.h 8.2 (Berkeley) 5/4/95 + */ + +#include <stddef.h> + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + size_t stacknleft; +}; + + +extern char *stacknxt; +extern size_t stacknleft; +extern char *sstrend; + +pointer ckmalloc(size_t); +pointer ckrealloc(pointer, size_t); +char *savestr(const char *); +pointer stalloc(size_t); +void stunalloc(pointer); +void pushstackmark(struct stackmark *mark, size_t len); +void setstackmark(struct stackmark *); +void popstackmark(struct stackmark *); +void growstackblock(void); +void *growstackstr(void); +char *makestrspace(size_t, char *); +char *stnputs(const char *, size_t, char *); +char *stputs(const char *, char *); + + +static inline void grabstackblock(size_t len) +{ + stalloc(len); +} + +static inline char *_STPUTC(int c, char *p) { + if (p == sstrend) + p = growstackstr(); + *p++ = c; + return p; +} + +#define stackblock() ((void *)stacknxt) +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) ((p) = stackblock()) +#define STPUTC(c, p) ((p) = _STPUTC((c), (p))) +#define CHECKSTRSPACE(n, p) \ + ({ \ + char *q = (p); \ + size_t l = (n); \ + size_t m = sstrend - q; \ + if (l > m) \ + (p) = makestrspace(l, q); \ + 0; \ + }) +#define USTPUTC(c, p) (*p++ = (c)) +#define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0')) +#define STUNPUTC(p) (--p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount)) + +#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock()) +#define ungrabstackstr(s, p) stunalloc((s)) +#define stackstrend() ((void *)sstrend) + +#define ckfree(p) free((pointer)(p)) diff --git a/usr/dash/miscbltin.c b/usr/dash/miscbltin.c new file mode 100644 index 0000000..09282be --- /dev/null +++ b/usr/dash/miscbltin.c @@ -0,0 +1,583 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +/* + * Miscelaneous builtins. + */ + +#include <sys/types.h> /* quad_t */ +#include <sys/param.h> /* BSD4_4 */ +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <inttypes.h> +#include <time.h> /* strtotimeval() */ + +#include "shell.h" +#include "options.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "miscbltin.h" +#include "mystring.h" +#include "main.h" +#include "expand.h" +#include "parser.h" +#include "trap.h" + +#undef rflag + + +/** handle one line of the read command. + * more fields than variables -> remainder shall be part of last variable. + * less fields than variables -> remaining variables unset. + * + * @param line complete line of input + * @param ap argument (variable) list + * @param len length of line including trailing '\0' + */ +static void +readcmd_handle_line(char *s, char **ap) +{ + struct arglist arglist; + struct strlist *sl; + char *backup; + char *line; + + /* ifsbreakup will fiddle with stack region... */ + line = stackblock(); + s = grabstackstr(s); + + /* need a copy, so that delimiters aren't lost + * in case there are more fields than variables */ + backup = sstrdup(line); + + arglist.lastp = &arglist.list; + + ifsbreakup(s, &arglist); + *arglist.lastp = NULL; + ifsfree(); + + sl = arglist.list; + + do { + if (!sl) { + /* nullify remaining arguments */ + do { + setvar(*ap, nullstr, 0); + } while (*++ap); + + return; + } + + /* remaining fields present, but no variables left. */ + if (!ap[1] && sl->next) { + size_t offset; + char *remainder; + + /* FIXME little bit hacky, assuming that ifsbreakup + * will not modify the length of the string */ + offset = sl->text - s; + remainder = backup + offset; + rmescapes(remainder); + setvar(*ap, remainder, 0); + + return; + } + + /* set variable to field */ + rmescapes(sl->text); + setvar(*ap, sl->text, 0); + sl = sl->next; + } while (*++ap); +} + +/* + * The read builtin. The -e option causes backslashes to escape the + * following character. The -p option followed by an argument prompts + * with the argument. + * + * This uses unbuffered input, which may be avoidable in some cases. + */ + +int +readcmd(int argc, char **argv) +{ + char **ap; + char c; + int rflag; + char *prompt; + char *p; + int startloc; + int newloc; + int status; + int timeout; + int i; + fd_set set; + struct timeval ts, t0, t1, to; + + ts.tv_sec = ts.tv_usec = 0; + + rflag = 0; + timeout = 0; + prompt = NULL; + while ((i = nextopt("p:rt:")) != '\0') { + switch(i) { + case 'p': + prompt = optionarg; + break; + case 't': + p = strtotimeval(optionarg, &ts); + if (*p || (!ts.tv_sec && !ts.tv_usec)) + sh_error("invalid timeout"); + timeout = 1; + break; + case 'r': + rflag = 1; + break; + default: + break; + } + } + if (prompt && isatty(0)) { + out2str(prompt); +#ifdef FLUSHERR + flushall(); +#endif + } + if (*(ap = argptr) == NULL) + sh_error("arg count"); + + status = 0; + if (timeout) { + gettimeofday(&t0, NULL); + + /* ts += t0; */ + ts.tv_usec += t0.tv_usec; + while (ts.tv_usec >= 1000000) { + ts.tv_sec++; + ts.tv_usec -= 1000000; + } + ts.tv_sec += t0.tv_sec; + } + STARTSTACKSTR(p); + + goto start; + + for (;;) { + if (timeout) { + gettimeofday(&t1, NULL); + if (t1.tv_sec > ts.tv_sec || + (t1.tv_sec == ts.tv_sec && + t1.tv_usec >= ts.tv_usec)) { + status = 1; + break; /* Timeout! */ + } + + /* to = ts - t1; */ + if (ts.tv_usec >= t1.tv_usec) { + to.tv_usec = ts.tv_usec - t1.tv_usec; + to.tv_sec = ts.tv_sec - t1.tv_sec; + } else { + to.tv_usec = ts.tv_usec - t1.tv_usec + 1000000; + to.tv_sec = ts.tv_sec - t1.tv_sec - 1; + } + + FD_ZERO(&set); + FD_SET(0, &set); + if (select(1, &set, NULL, NULL, &to) != 1) { + status = 1; + break; /* Timeout! */ + } + } + switch (read(0, &c, 1)) { + case 1: + break; + default: + if (errno == EINTR && !pendingsigs) + continue; + /* fall through */ + case 0: + status = 1; + goto out; + } + if (c == '\0') + continue; + if (newloc >= startloc) { + if (c == '\n') + goto resetbs; + goto put; + } + if (!rflag && c == '\\') { + newloc = p - (char *)stackblock(); + continue; + } + if (c == '\n') + break; +put: + CHECKSTRSPACE(2, p); + if (strchr(qchars, c)) + USTPUTC(CTLESC, p); + USTPUTC(c, p); + + if (newloc >= startloc) { +resetbs: + recordregion(startloc, newloc, 0); +start: + startloc = p - (char *)stackblock(); + newloc = startloc - 1; + } + } +out: + recordregion(startloc, p - (char *)stackblock(), 0); + STACKSTRNUL(p); + readcmd_handle_line(p + 1, ap); + return status; +} + + + +/* + * umask builtin + * + * This code was ripped from pdksh 5.2.14 and hacked for use with + * dash by Herbert Xu. + * + * Public domain. + */ + +int +umaskcmd(int argc, char **argv) +{ + char *ap; + int mask; + int i; + int symbolic_mode = 0; + + while ((i = nextopt("S")) != '\0') { + symbolic_mode = 1; + } + + INTOFF; + mask = umask(0); + umask(mask); + INTON; + + if ((ap = *argptr) == NULL) { + if (symbolic_mode) { + char buf[18]; + int j; + + mask = ~mask; + ap = buf; + for (i = 0; i < 3; i++) { + *ap++ = "ugo"[i]; + *ap++ = '='; + for (j = 0; j < 3; j++) + if (mask & (1 << (8 - (3*i + j)))) + *ap++ = "rwx"[j]; + *ap++ = ','; + } + ap[-1] = '\0'; + out1fmt("%s\n", buf); + } else { + out1fmt("%.4o\n", mask); + } + } else { + int new_mask; + + if (isdigit((unsigned char) *ap)) { + new_mask = 0; + do { + if (*ap >= '8' || *ap < '0') + sh_error(illnum, *argptr); + new_mask = (new_mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + } else { + int positions, new_val; + char op; + + mask = ~mask; + new_mask = mask; + positions = 0; + while (*ap) { + while (*ap && strchr("augo", *ap)) + switch (*ap++) { + case 'a': positions |= 0111; break; + case 'u': positions |= 0100; break; + case 'g': positions |= 0010; break; + case 'o': positions |= 0001; break; + } + if (!positions) + positions = 0111; /* default is a */ + if (!strchr("=+-", op = *ap)) + break; + ap++; + new_val = 0; + while (*ap && strchr("rwxugoXs", *ap)) + switch (*ap++) { + case 'r': new_val |= 04; break; + case 'w': new_val |= 02; break; + case 'x': new_val |= 01; break; + case 'u': new_val |= mask >> 6; + break; + case 'g': new_val |= mask >> 3; + break; + case 'o': new_val |= mask >> 0; + break; + case 'X': if (mask & 0111) + new_val |= 01; + break; + case 's': /* ignored */ + break; + } + new_val = (new_val & 07) * positions; + switch (op) { + case '-': + new_mask &= ~new_val; + break; + case '=': + new_mask = new_val + | (new_mask & ~(positions * 07)); + break; + case '+': + new_mask |= new_val; + } + if (*ap == ',') { + positions = 0; + ap++; + } else if (!strchr("=+-", *ap)) + break; + } + if (*ap) { + sh_error("Illegal mode: %s", *argptr); + return 1; + } + new_mask = ~new_mask; + } + umask(new_mask); + } + return 0; +} + +#ifdef HAVE_GETRLIMIT +/* + * ulimit builtin + * + * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and + * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with + * ash by J.T. Conklin. + * + * Public domain. + */ + +struct limits { + const char *name; + int cmd; + int factor; /* multiply by to get rlim_{cur,max} values */ + char option; +}; + +static const struct limits limits[] = { +#ifdef RLIMIT_CPU + { "time(seconds)", RLIMIT_CPU, 1, 't' }, +#endif +#ifdef RLIMIT_FSIZE + { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, +#endif +#ifdef RLIMIT_DATA + { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, +#endif +#ifdef RLIMIT_STACK + { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, +#endif +#ifdef RLIMIT_CORE + { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, +#endif +#ifdef RLIMIT_RSS + { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, +#endif +#ifdef RLIMIT_MEMLOCK + { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, +#endif +#ifdef RLIMIT_NPROC + { "process", RLIMIT_NPROC, 1, 'p' }, +#endif +#ifdef RLIMIT_NOFILE + { "nofiles", RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_AS + { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' }, +#endif +#ifdef RLIMIT_LOCKS + { "locks", RLIMIT_LOCKS, 1, 'w' }, +#endif +#ifdef RLIMIT_RTPRIO + { "rtprio", RLIMIT_RTPRIO, 1, 'r' }, +#endif + { (char *) 0, 0, 0, '\0' } +}; + +enum limtype { SOFT = 0x1, HARD = 0x2 }; + +static void printlim(enum limtype how, const struct rlimit *limit, + const struct limits *l) +{ + rlim_t val; + + val = limit->rlim_max; + if (how & SOFT) + val = limit->rlim_cur; + + if (val == RLIM_INFINITY) + out1fmt("unlimited\n"); + else { + val /= l->factor; + out1fmt("%" PRIdMAX "\n", (intmax_t) val); + } +} + +int +ulimitcmd(int argc, char **argv) +{ + int c; + rlim_t val = 0; + enum limtype how = SOFT | HARD; + const struct limits *l; + int set, all = 0; + int optc, what; + struct rlimit limit; + + what = 'f'; + while ((optc = nextopt("HSa" +#ifdef RLIMIT_CPU + "t" +#endif +#ifdef RLIMIT_FSIZE + "f" +#endif +#ifdef RLIMIT_DATA + "d" +#endif +#ifdef RLIMIT_STACK + "s" +#endif +#ifdef RLIMIT_CORE + "c" +#endif +#ifdef RLIMIT_RSS + "m" +#endif +#ifdef RLIMIT_MEMLOCK + "l" +#endif +#ifdef RLIMIT_NPROC + "p" +#endif +#ifdef RLIMIT_NOFILE + "n" +#endif +#ifdef RLIMIT_AS + "v" +#endif +#ifdef RLIMIT_LOCKS + "w" +#endif + )) != '\0') + switch (optc) { + case 'H': + how = HARD; + break; + case 'S': + how = SOFT; + break; + case 'a': + all = 1; + break; + default: + what = optc; + } + + for (l = limits; l->option != what; l++) + ; + + set = *argptr ? 1 : 0; + if (set) { + char *p = *argptr; + + if (all || argptr[1]) + sh_error("too many arguments"); + if (strcmp(p, "unlimited") == 0) + val = RLIM_INFINITY; + else { + val = (rlim_t) 0; + + while ((c = *p++) >= '0' && c <= '9') + { + val = (val * 10) + (long)(c - '0'); + if (val < (rlim_t) 0) + break; + } + if (c) + sh_error("bad number"); + val *= l->factor; + } + } + if (all) { + for (l = limits; l->name; l++) { + getrlimit(l->cmd, &limit); + out1fmt("%-20s ", l->name); + printlim(how, &limit, l); + } + return 0; + } + + getrlimit(l->cmd, &limit); + if (set) { + if (how & HARD) + limit.rlim_max = val; + if (how & SOFT) + limit.rlim_cur = val; + if (setrlimit(l->cmd, &limit) < 0) + sh_error("error setting limit (%s)", strerror(errno)); + } else { + printlim(how, &limit, l); + } + return 0; +} +#endif diff --git a/usr/dash/miscbltin.h b/usr/dash/miscbltin.h new file mode 100644 index 0000000..dd9a8d1 --- /dev/null +++ b/usr/dash/miscbltin.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1997 Christos Zoulas. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. 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. + * 3. 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. + */ + +int readcmd(int, char **); +int umaskcmd(int, char **); +int ulimitcmd(int, char **); diff --git a/usr/dash/mkbuiltins b/usr/dash/mkbuiltins new file mode 100644 index 0000000..94b36b9 --- /dev/null +++ b/usr/dash/mkbuiltins @@ -0,0 +1,115 @@ +#!/bin/sh - +# $NetBSD: mkbuiltins,v 1.17 2002/11/24 22:35:41 christos Exp $ +# +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)mkbuiltins 8.2 (Berkeley) 5/4/95 + +tempfile=mktemp +if ! type tempfile > /dev/null 2>&1 && ! type mktemp > /dev/null 2>&1; then + _my_tempfile() + { + local index=0 + while test -f "${TMPDIR:-/tmp}/builtin.$$.$index"; do + index=`expr $index + 1` + done + + touch "${TMPDIR:-/tmp}/builtin.$$.$index" + echo "${TMPDIR:-/tmp}/builtin.$$.$index" + } + + tempfile="_my_tempfile" +elif ! type tempfile > /dev/null 2>&1; then + tempfile="mktemp ${TMPDIR:-/tmp}/builtin.XXXXXX" +fi + +trap 'rm -f $temp $temp2' EXIT +temp=$($tempfile) +temp2=$($tempfile) + +builtins=$1 + +exec > builtins.c +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include "shell.h" +#include "builtins.h" + +! +< $builtins sed '/^#/d; /^$/d' > $temp +awk '{ printf "int %s(int, char **);\n", $1}' $temp +echo ' +const struct builtincmd builtincmd[] = {' +awk '{ for (i = 2 ; i <= NF ; i++) { + line = $i "\t" $1 + if ($i ~ /^-/) + line = $(++i) "\t" line + print line + }}' $temp | LC_ALL=C sort -k 1,1 | tee $temp2 | awk '{ + opt = "" + if (NF > 2) { + opt = substr($2, 2) + $2 = $3 + } + printf "\t{ \"%s\", %s, %d },\n", $1, + (opt ~ /n/) ? "NULL" : $2, + (opt ~ /s/) + (opt ~ /[su]/) * 2 + (opt ~ /a/) * 4 + }' +echo '};' + +exec > builtins.h +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +! +sed 's/ -[a-z]*//' $temp2 | nl -b a -v 0 | LC_COLLATE=C sort -u -k 3,3 | +tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ | + awk '{ printf "#define %s (builtincmd + %d)\n", $3, $1}' +printf '\n#define NUMBUILTINS %d\n' $(wc -l < $temp2) +echo ' +#define BUILTIN_SPECIAL 0x1 +#define BUILTIN_REGULAR 0x2 +#define BUILTIN_ASSIGN 0x4 + +struct builtincmd { + const char *name; + int (*builtin)(int, char **); + unsigned flags; +}; + +extern const struct builtincmd builtincmd[];' diff --git a/usr/dash/mkinit.c b/usr/dash/mkinit.c new file mode 100644 index 0000000..9714bee --- /dev/null +++ b/usr/dash/mkinit.c @@ -0,0 +1,479 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +/* + * This program scans all the source files for code to handle various + * special events and combines this code into one file. This (allegedly) + * improves the structure of the program since there is no need for + * anyone outside of a module to know that that module performs special + * operations on particular events. + * + * Usage: mkinit sourcefile... + */ + + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> + + +/* + * OUTFILE is the name of the output file. Output is initially written + * to the file OUTTEMP, which is then moved to OUTFILE. + */ + +#define OUTFILE "init.c" +#define OUTTEMP "init.c.new" + + +/* + * A text structure is basicly just a string that grows as more characters + * are added onto the end of it. It is implemented as a linked list of + * blocks of characters. The routines addstr and addchar append a string + * or a single character, respectively, to a text structure. Writetext + * writes the contents of a text structure to a file. + */ + +#define BLOCKSIZE 512 + +struct text { + char *nextc; + int nleft; + struct block *start; + struct block *last; +}; + +struct block { + struct block *next; + char text[BLOCKSIZE]; +}; + + +/* + * There is one event structure for each event that mkinit handles. + */ + +struct event { + char *name; /* name of event (e.g. INIT) */ + char *routine; /* name of routine called on event */ + char *comment; /* comment describing routine */ + struct text code; /* code for handling event */ +}; + + +char writer[] = "\ +/*\n\ + * This file was generated by the mkinit program.\n\ + */\n\ +\n"; + +char init[] = "\ +/*\n\ + * Initialization code.\n\ + */\n"; + +char reset[] = "\ +/*\n\ + * This routine is called when an error or an interrupt occurs in an\n\ + * interactive shell and control is returned to the main command loop.\n\ + */\n"; + + +struct event event[] = { + {"INIT", "init", init}, + {"RESET", "reset", reset}, + {NULL, NULL} +}; + + +char *curfile; /* current file */ +int linno; /* current line */ +char *header_files[200]; /* list of header files */ +struct text defines; /* #define statements */ +struct text decls; /* declarations */ +int amiddecls; /* for formatting */ + + +void readfile(char *); +int match(char *, char *); +int gooddefine(char *); +void doevent(struct event *, FILE *, char *); +void doinclude(char *); +void dodecl(char *, FILE *); +void output(void); +void addstr(char *, struct text *); +void addchar(int, struct text *); +void writetext(struct text *, FILE *); +FILE *ckfopen(char *, char *); +void *ckmalloc(int); +char *savestr(char *); +static void error(char *); +int main(int, char **); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) + +int +main(int argc, char **argv) +{ + char **ap; + + header_files[0] = "\"shell.h\""; + header_files[1] = "\"mystring.h\""; + header_files[2] = "\"init.h\""; + for (ap = argv + 1 ; *ap ; ap++) + readfile(*ap); + output(); + rename(OUTTEMP, OUTFILE); + exit(0); + /* NOTREACHED */ +} + + +/* + * Parse an input file. + */ + +void +readfile(char *fname) +{ + FILE *fp; + char line[1024]; + struct event *ep; + + fp = ckfopen(fname, "r"); + curfile = fname; + linno = 0; + amiddecls = 0; + while (fgets(line, sizeof line, fp) != NULL) { + linno++; + for (ep = event ; ep->name ; ep++) { + if (line[0] == ep->name[0] && match(ep->name, line)) { + doevent(ep, fp, fname); + break; + } + } + if (line[0] == 'I' && match("INCLUDE", line)) + doinclude(line); + if (line[0] == 'M' && match("MKINIT", line)) + dodecl(line, fp); + if (line[0] == '#' && gooddefine(line)) { + char *cp; + char line2[1024]; + static const char undef[] = "#undef "; + + strcpy(line2, line); + memcpy(line2, undef, sizeof(undef) - 1); + cp = line2 + sizeof(undef) - 1; + while(*cp && (*cp == ' ' || *cp == '\t')) + cp++; + while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n') + cp++; + *cp++ = '\n'; *cp = '\0'; + addstr(line2, &defines); + addstr(line, &defines); + } + } + fclose(fp); +} + + +int +match(char *name, char *line) +{ + char *p, *q; + + p = name, q = line; + while (*p) { + if (*p++ != *q++) + return 0; + } + if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n') + return 0; + return 1; +} + + +int +gooddefine(char *line) +{ + char *p; + + if (! match("#define", line)) + return 0; /* not a define */ + p = line + 7; + while (*p == ' ' || *p == '\t') + p++; + while (*p != ' ' && *p != '\t') { + if (*p == '(') + return 0; /* macro definition */ + p++; + } + while (*p != '\n' && *p != '\0') + p++; + if (p[-1] == '\\') + return 0; /* multi-line definition */ + return 1; +} + + +void +doevent(struct event *ep, FILE *fp, char *fname) +{ + char line[1024]; + int indent; + char *p; + + sprintf(line, "\n /* from %s: */\n", fname); + addstr(line, &ep->code); + addstr(" {\n", &ep->code); + for (;;) { + linno++; + if (fgets(line, sizeof line, fp) == NULL) + error("Unexpected EOF"); + if (equal(line, "}\n")) + break; + indent = 6; + for (p = line ; *p == '\t' ; p++) + indent += 8; + for ( ; *p == ' ' ; p++) + indent++; + if (*p == '\n' || *p == '#') + indent = 0; + while (indent >= 8) { + addchar('\t', &ep->code); + indent -= 8; + } + while (indent > 0) { + addchar(' ', &ep->code); + indent--; + } + addstr(p, &ep->code); + } + addstr(" }\n", &ep->code); +} + + +void +doinclude(char *line) +{ + char *p; + char *name; + char **pp; + + for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++); + if (*p == '\0') + error("Expecting '\"' or '<'"); + name = p; + while (*p != ' ' && *p != '\t' && *p != '\n') + p++; + if (p[-1] != '"' && p[-1] != '>') + error("Missing terminator"); + *p = '\0'; + + /* name now contains the name of the include file */ + for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++); + if (*pp == NULL) + *pp = savestr(name); +} + + +void +dodecl(char *line1, FILE *fp) +{ + char line[1024]; + char *p, *q; + + if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */ + addchar('\n', &decls); + do { + linno++; + if (fgets(line, sizeof line, fp) == NULL) + error("Unterminated structure declaration"); + addstr(line, &decls); + } while (line[0] != '}'); + amiddecls = 0; + } else { + if (! amiddecls) + addchar('\n', &decls); + q = NULL; + for (p = line1 + 6 ; *p && strchr("=/\n", *p) == NULL; p++) + continue; + if (*p == '=') { /* eliminate initialization */ + for (q = p ; *q && *q != ';' ; q++); + if (*q == '\0') + q = NULL; + else { + while (p[-1] == ' ') + p--; + *p = '\0'; + } + } + addstr("extern", &decls); + addstr(line1 + 6, &decls); + if (q != NULL) + addstr(q, &decls); + amiddecls = 1; + } +} + + + +/* + * Write the output to the file OUTTEMP. + */ + +void +output(void) +{ + FILE *fp; + char **pp; + struct event *ep; + + fp = ckfopen(OUTTEMP, "w"); + fputs(writer, fp); + for (pp = header_files ; *pp ; pp++) + fprintf(fp, "#include %s\n", *pp); + fputs("\n\n\n", fp); + writetext(&defines, fp); + fputs("\n\n", fp); + writetext(&decls, fp); + for (ep = event ; ep->name ; ep++) { + fputs("\n\n\n", fp); + fputs(ep->comment, fp); + fprintf(fp, "\nvoid\n%s() {\n", ep->routine); + writetext(&ep->code, fp); + fprintf(fp, "}\n"); + } + fclose(fp); +} + + +/* + * A text structure is simply a block of text that is kept in memory. + * Addstr appends a string to the text struct, and addchar appends a single + * character. + */ + +void +addstr(char *s, struct text *text) +{ + while (*s) { + if (--text->nleft < 0) + addchar(*s++, text); + else + *text->nextc++ = *s++; + } +} + + +void +addchar(int c, struct text *text) +{ + struct block *bp; + + if (--text->nleft < 0) { + bp = ckmalloc(sizeof *bp); + if (text->start == NULL) + text->start = bp; + else + text->last->next = bp; + text->last = bp; + text->nextc = bp->text; + text->nleft = BLOCKSIZE - 1; + } + *text->nextc++ = c; +} + +/* + * Write the contents of a text structure to a file. + */ +void +writetext(struct text *text, FILE *fp) +{ + struct block *bp; + + if (text->start != NULL) { + for (bp = text->start ; bp != text->last ; bp = bp->next) { + if ((fwrite(bp->text, sizeof (char), BLOCKSIZE, fp)) != BLOCKSIZE) + error("Can't write data\n"); + } + if ((fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp)) != (BLOCKSIZE - text->nleft)) + error("Can't write data\n"); + } +} + +FILE * +ckfopen(char *file, char *mode) +{ + FILE *fp; + + if ((fp = fopen(file, mode)) == NULL) { + fprintf(stderr, "Can't open %s\n", file); + exit(2); + } + return fp; +} + +void * +ckmalloc(int nbytes) +{ + char *p; + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + +char * +savestr(char *s) +{ + char *p; + + p = ckmalloc(strlen(s) + 1); + strcpy(p, s); + return p; +} + +static void +error(char *msg) +{ + if (curfile != NULL) + fprintf(stderr, "%s:%d: ", curfile, linno); + fprintf(stderr, "%s\n", msg); + exit(2); + /* NOTREACHED */ +} diff --git a/usr/dash/mknodes.c b/usr/dash/mknodes.c new file mode 100644 index 0000000..1903a60 --- /dev/null +++ b/usr/dash/mknodes.c @@ -0,0 +1,448 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +/* + * This program reads the nodetypes file and nodes.c.pat file. It generates + * the files nodes.h and nodes.c. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#define MAXTYPES 50 /* max number of node types */ +#define MAXFIELDS 20 /* max fields in a structure */ +#define BUFLEN 100 /* size of character buffers */ + +/* field types */ +#define T_NODE 1 /* union node *field */ +#define T_NODELIST 2 /* struct nodelist *field */ +#define T_STRING 3 +#define T_INT 4 /* int field */ +#define T_OTHER 5 /* other */ +#define T_TEMP 6 /* don't copy this field */ + + +struct field { /* a structure field */ + char *name; /* name of field */ + int type; /* type of field */ + char *decl; /* declaration of field */ +}; + + +struct str { /* struct representing a node structure */ + char *tag; /* structure tag */ + int nfields; /* number of fields in the structure */ + struct field field[MAXFIELDS]; /* the fields of the structure */ + int done; /* set if fully parsed */ +}; + + +static int ntypes; /* number of node types */ +static char *nodename[MAXTYPES]; /* names of the nodes */ +static struct str *nodestr[MAXTYPES]; /* type of structure used by the node */ +static int nstr; /* number of structures */ +static struct str str[MAXTYPES]; /* the structures */ +static struct str *curstr; /* current structure */ +static FILE *infp; +static char line[1024]; +static int linno; +static char *linep; + +static void parsenode(void); +static void parsefield(void); +static void output(char *); +static void outsizes(FILE *); +static void outfunc(FILE *, int); +static void indent(int, FILE *); +static int nextfield(char *); +static void skipbl(void); +static int readline(void); +static void error(const char *, ...); +static char *savestr(const char *); +int main(int, char **); + + +int +main(int argc, char **argv) +{ + + /* + * some versions of linux complain: initializer element is not + * constant if this is done at compile time. + */ + infp = stdin; + + if (argc != 3) + error("usage: mknodes file"); + if ((infp = fopen(argv[1], "r")) == NULL) + error("Can't open %s", argv[1]); + while (readline()) { + if (line[0] == ' ' || line[0] == '\t') + parsefield(); + else if (line[0] != '\0') + parsenode(); + } + output(argv[2]); + exit(0); + /* NOTREACHED */ +} + + + +static void +parsenode(void) +{ + char name[BUFLEN]; + char tag[BUFLEN]; + struct str *sp; + + if (curstr && curstr->nfields > 0) + curstr->done = 1; + nextfield(name); + if (! nextfield(tag)) + error("Tag expected"); + if (*linep != '\0') + error("Garbage at end of line"); + nodename[ntypes] = savestr(name); + for (sp = str ; sp < str + nstr ; sp++) { + if (strcmp(sp->tag, tag) == 0) + break; + } + if (sp >= str + nstr) { + sp->tag = savestr(tag); + sp->nfields = 0; + curstr = sp; + nstr++; + } + nodestr[ntypes] = sp; + ntypes++; +} + + +static void +parsefield(void) +{ + char name[BUFLEN]; + char type[BUFLEN]; + char decl[2 * BUFLEN]; + struct field *fp; + + if (curstr == NULL || curstr->done) + error("No current structure to add field to"); + if (! nextfield(name)) + error("No field name"); + if (! nextfield(type)) + error("No field type"); + fp = &curstr->field[curstr->nfields]; + fp->name = savestr(name); + if (strcmp(type, "nodeptr") == 0) { + fp->type = T_NODE; + sprintf(decl, "union node *%s", name); + } else if (strcmp(type, "nodelist") == 0) { + fp->type = T_NODELIST; + sprintf(decl, "struct nodelist *%s", name); + } else if (strcmp(type, "string") == 0) { + fp->type = T_STRING; + sprintf(decl, "char *%s", name); + } else if (strcmp(type, "int") == 0) { + fp->type = T_INT; + sprintf(decl, "int %s", name); + } else if (strcmp(type, "other") == 0) { + fp->type = T_OTHER; + } else if (strcmp(type, "temp") == 0) { + fp->type = T_TEMP; + } else { + error("Unknown type %s", type); + } + if (fp->type == T_OTHER || fp->type == T_TEMP) { + skipbl(); + fp->decl = savestr(linep); + } else { + if (*linep) + error("Garbage at end of line"); + fp->decl = savestr(decl); + } + curstr->nfields++; +} + + +char writer[] = "\ +/*\n\ + * This file was generated by the mknodes program.\n\ + */\n\ +\n"; + +static void +output(char *file) +{ + FILE *hfile; + FILE *cfile; + FILE *patfile; + int i; + struct str *sp; + struct field *fp; + char *p; + + if ((patfile = fopen(file, "r")) == NULL) + error("Can't open %s", file); + if ((hfile = fopen("nodes.h", "w")) == NULL) + error("Can't create nodes.h"); + if ((cfile = fopen("nodes.c", "w")) == NULL) + error("Can't create nodes.c"); + fputs(writer, hfile); + for (i = 0 ; i < ntypes ; i++) + fprintf(hfile, "#define %s %d\n", nodename[i], i); + fputs("\n\n\n", hfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, "struct %s {\n", sp->tag); + for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) { + fprintf(hfile, " %s;\n", fp->decl); + } + fputs("};\n\n\n", hfile); + } + fputs("union node {\n", hfile); + fprintf(hfile, " int type;\n"); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag); + } + fputs("};\n\n\n", hfile); + fputs("struct nodelist {\n", hfile); + fputs("\tstruct nodelist *next;\n", hfile); + fputs("\tunion node *n;\n", hfile); + fputs("};\n\n\n", hfile); + fputs("struct funcnode {\n", hfile); + fputs("\tint count;\n", hfile); + fputs("\tunion node n;\n", hfile); + fputs("};\n\n\n", hfile); + fputs("struct funcnode *copyfunc(union node *);\n", hfile); + fputs("void freefunc(struct funcnode *);\n", hfile); + + fputs(writer, cfile); + while (fgets(line, sizeof line, patfile) != NULL) { + for (p = line ; *p == ' ' || *p == '\t' ; p++); + if (strcmp(p, "%SIZES\n") == 0) + outsizes(cfile); + else if (strcmp(p, "%CALCSIZE\n") == 0) + outfunc(cfile, 1); + else if (strcmp(p, "%COPY\n") == 0) + outfunc(cfile, 0); + else + fputs(line, cfile); + } +} + + + +static void +outsizes(FILE *cfile) +{ + int i; + + fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes); + for (i = 0 ; i < ntypes ; i++) { + fprintf(cfile, " SHELL_ALIGN(sizeof (struct %s)),\n", + nodestr[i]->tag); + } + fprintf(cfile, "};\n"); +} + + +static void +outfunc(FILE *cfile, int calcsize) +{ + struct str *sp; + struct field *fp; + int i; + + fputs(" if (n == NULL)\n", cfile); + if (calcsize) + fputs(" return;\n", cfile); + else + fputs(" return NULL;\n", cfile); + if (calcsize) + fputs(" funcblocksize += nodesize[n->type];\n", cfile); + else { + fputs(" new = funcblock;\n", cfile); + fputs(" funcblock = (char *) funcblock + nodesize[n->type];\n", cfile); + } + fputs(" switch (n->type) {\n", cfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + for (i = 0 ; i < ntypes ; i++) { + if (nodestr[i] == sp) + fprintf(cfile, " case %s:\n", nodename[i]); + } + for (i = sp->nfields ; --i >= 1 ; ) { + fp = &sp->field[i]; + switch (fp->type) { + case T_NODE: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "calcsize(n->%s.%s);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynode(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_NODELIST: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "sizenodelist(n->%s.%s);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_STRING: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "funcstringsize += strlen(n->%s.%s) + 1;\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_INT: + case T_OTHER: + if (! calcsize) { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = n->%s.%s;\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + } + } + indent(12, cfile); + fputs("break;\n", cfile); + } + fputs(" };\n", cfile); + if (! calcsize) + fputs(" new->type = n->type;\n", cfile); +} + + +static void +indent(int amount, FILE *fp) +{ + while (amount >= 8) { + putc('\t', fp); + amount -= 8; + } + while (--amount >= 0) { + putc(' ', fp); + } +} + + +static int +nextfield(char *buf) +{ + char *p, *q; + + p = linep; + while (*p == ' ' || *p == '\t') + p++; + q = buf; + while (*p != ' ' && *p != '\t' && *p != '\0') + *q++ = *p++; + *q = '\0'; + linep = p; + return (q > buf); +} + + +static void +skipbl(void) +{ + while (*linep == ' ' || *linep == '\t') + linep++; +} + + +static int +readline(void) +{ + char *p; + + if (fgets(line, 1024, infp) == NULL) + return 0; + for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++); + while (p > line && (p[-1] == ' ' || p[-1] == '\t')) + p--; + *p = '\0'; + linep = line; + linno++; + if (p - line > BUFLEN) + error("Line too long"); + return 1; +} + + + +static void +error(const char *msg, ...) +{ + va_list va; + + va_start(va, msg); + + (void) fprintf(stderr, "line %d: ", linno); + (void) vfprintf(stderr, msg, va); + (void) fputc('\n', stderr); + + va_end(va); + + exit(2); + /* NOTREACHED */ +} + + + +static char * +savestr(const char *s) +{ + char *p; + + if ((p = malloc(strlen(s) + 1)) == NULL) + error("Out of space"); + (void) strcpy(p, s); + return p; +} diff --git a/usr/dash/mksyntax.c b/usr/dash/mksyntax.c new file mode 100644 index 0000000..a23c18c --- /dev/null +++ b/usr/dash/mksyntax.c @@ -0,0 +1,315 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +/* + * This program creates syntax.h and syntax.c. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include "parser.h" + + +struct synclass { + char *name; + char *comment; +}; + +/* Syntax classes */ +struct synclass synclass[] = { + { "CWORD", "character is nothing special" }, + { "CNL", "newline character" }, + { "CBACK", "a backslash character" }, + { "CSQUOTE", "single quote" }, + { "CDQUOTE", "double quote" }, + { "CENDQUOTE", "a terminating quote" }, + { "CBQUOTE", "backwards single quote" }, + { "CVAR", "a dollar sign" }, + { "CENDVAR", "a '}' character" }, + { "CLP", "a left paren in arithmetic" }, + { "CRP", "a right paren in arithmetic" }, + { "CEOF", "end of file" }, + { "CCTL", "like CWORD, except it must be escaped" }, + { "CSPCL", "these terminate a word" }, + { "CIGN", "character should be ignored" }, + { NULL, NULL } +}; + + +/* + * Syntax classes for is_ functions. Warning: if you add new classes + * you may have to change the definition of the is_in_name macro. + */ +struct synclass is_entry[] = { + { "ISDIGIT", "a digit" }, + { "ISUPPER", "an upper case letter" }, + { "ISLOWER", "a lower case letter" }, + { "ISUNDER", "an underscore" }, + { "ISSPECL", "the name of a special parameter" }, + { NULL, NULL } +}; + +static char writer[] = "\ +/*\n\ + * This file was generated by the mksyntax program.\n\ + */\n\ +\n"; + + +static FILE *cfile; +static FILE *hfile; +static char *syntax[513]; + +static void filltable(char *); +static void init(void); +static void add(char *, char *); +static void print(char *); +static void output_type_macros(void); +int main(int, char **); + +int +main(int argc, char **argv) +{ + int i; + char buf[80]; + int pos; + + /* Create output files */ + if ((cfile = fopen("syntax.c", "w")) == NULL) { + perror("syntax.c"); + exit(2); + } + if ((hfile = fopen("syntax.h", "w")) == NULL) { + perror("syntax.h"); + exit(2); + } + fputs(writer, hfile); + fputs(writer, cfile); + + fputs("#include <ctype.h>\n", hfile); + fputs("\n", hfile); + fputs("#ifdef CEOF\n", hfile); + fputs("#undef CEOF\n", hfile); + fputs("#endif\n", hfile); + fputs("\n", hfile); + + /* Generate the #define statements in the header file */ + fputs("/* Syntax classes */\n", hfile); + for (i = 0 ; synclass[i].name ; i++) { + sprintf(buf, "#define %s %d", synclass[i].name, i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", synclass[i].comment); + } + putc('\n', hfile); + fputs("/* Syntax classes for is_ functions */\n", hfile); + for (i = 0 ; is_entry[i].name ; i++) { + sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", is_entry[i].comment); + } + putc('\n', hfile); + fprintf(hfile, "#define SYNBASE %d\n", 130); + fprintf(hfile, "#define PEOF %d\n\n", -130); + fprintf(hfile, "#define PEOA %d\n\n", -129); + putc('\n', hfile); + fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile); + fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile); + fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile); + fputs("#define ARISYNTAX (arisyntax + SYNBASE)\n", hfile); + putc('\n', hfile); + output_type_macros(); /* is_digit, etc. */ + putc('\n', hfile); + + /* Generate the syntax tables. */ + fputs("#include \"shell.h\"\n", cfile); + fputs("#include \"syntax.h\"\n\n", cfile); + init(); + fputs("/* syntax table used when not in quotes */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("'", "CSQUOTE"); + add("\"", "CDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("<>();&| \t", "CSPCL"); + syntax[1] = "CSPCL"; + print("basesyntax"); + init(); + fputs("\n/* syntax table used when in double quotes */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("\"", "CENDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ + add("!*?[=~:/-]", "CCTL"); + print("dqsyntax"); + init(); + fputs("\n/* syntax table used when in single quotes */\n", cfile); + add("\n", "CNL"); + add("'", "CENDQUOTE"); + /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ + add("!*?[=~:/-]\\", "CCTL"); + print("sqsyntax"); + init(); + fputs("\n/* syntax table used when in arithmetic */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("(", "CLP"); + add(")", "CRP"); + print("arisyntax"); + filltable("0"); + fputs("\n/* character classification table */\n", cfile); + add("0123456789", "ISDIGIT"); + add("abcdefghijklmnopqrstucvwxyz", "ISLOWER"); + add("ABCDEFGHIJKLMNOPQRSTUCVWXYZ", "ISUPPER"); + add("_", "ISUNDER"); + add("#?$!-*@", "ISSPECL"); + print("is_type"); + exit(0); + /* NOTREACHED */ +} + + + +/* + * Clear the syntax table. + */ + +static void +filltable(char *dftval) +{ + int i; + + for (i = 0 ; i < 258; i++) + syntax[i] = dftval; +} + + +/* + * Initialize the syntax table with default values. + */ + +static void +init(void) +{ + int ctl; + + filltable("CWORD"); + syntax[0] = "CEOF"; + syntax[1] = "CIGN"; + for (ctl = CTL_FIRST; ctl <= CTL_LAST; ctl++ ) + syntax[130 + ctl] = "CCTL"; +} + + +/* + * Add entries to the syntax table. + */ + +static void +add(char *p, char *type) +{ + while (*p) + syntax[(signed char)*p++ + 130] = type; +} + + + +/* + * Output the syntax table. + */ + +static void +print(char *name) +{ + int i; + int col; + + fprintf(hfile, "extern const char %s[];\n", name); + fprintf(cfile, "const char %s[] = {\n", name); + col = 0; + for (i = 0 ; i < 258; i++) { + if (i == 0) { + fputs(" ", cfile); + } else if ((i & 03) == 0) { + fputs(",\n ", cfile); + col = 0; + } else { + putc(',', cfile); + while (++col < 9 * (i & 03)) + putc(' ', cfile); + } + fputs(syntax[i], cfile); + col += strlen(syntax[i]); + } + fputs("\n};\n", cfile); +} + + + +/* + * Output character classification macros (e.g. is_digit). If digits are + * contiguous, we can test for them quickly. + */ + +static char *macro[] = { + "#define is_digit(c)\t((unsigned)((c) - '0') <= 9)\n", + "#define is_alpha(c)\tisalpha((unsigned char)(c))\n", + "#define is_name(c)\t((c) == '_' || isalpha((unsigned char)(c)))\n", + "#define is_in_name(c)\t((c) == '_' || isalnum((unsigned char)(c)))\n", + "#define is_special(c)\t((is_type+SYNBASE)[(signed char)(c)] & (ISSPECL|ISDIGIT))\n", + NULL +}; + +static void +output_type_macros(void) +{ + char **pp; + + for (pp = macro ; *pp ; pp++) + fputs(*pp, hfile); + fputs("#define digit_val(c)\t((c) - '0')\n", hfile); +} diff --git a/usr/dash/mktokens b/usr/dash/mktokens new file mode 100644 index 0000000..8fbcef1 --- /dev/null +++ b/usr/dash/mktokens @@ -0,0 +1,92 @@ +#!/bin/sh - +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)mktokens 8.1 (Berkeley) 5/31/93 + +# The following is a list of tokens. The second column is nonzero if the +# token marks the end of a list. The third column is the name to print in +# error messages. + +cat > /tmp/ka$$ <<\! +TEOF 1 end of file +TNL 0 newline +TSEMI 0 ";" +TBACKGND 0 "&" +TAND 0 "&&" +TOR 0 "||" +TPIPE 0 "|" +TLP 0 "(" +TRP 1 ")" +TENDCASE 1 ";;" +TENDBQUOTE 1 "`" +TREDIR 0 redirection +TWORD 0 word +TNOT 0 "!" +TCASE 0 "case" +TDO 1 "do" +TDONE 1 "done" +TELIF 1 "elif" +TELSE 1 "else" +TESAC 1 "esac" +TFI 1 "fi" +TFOR 0 "for" +TIF 0 "if" +TIN 0 "in" +TTHEN 1 "then" +TUNTIL 0 "until" +TWHILE 0 "while" +TBEGIN 0 "{" +TEND 1 "}" +! +nl=`wc -l /tmp/ka$$` + +awk '{print "#define " $1 " " NR-1}' /tmp/ka$$ +echo ' +/* Array indicating which tokens mark the end of a list */ +const char tokendlist[] = {' +awk '{print "\t" $2 ","}' /tmp/ka$$ +echo '}; + +const char *const tokname[] = {' +sed -e 's/"/\\"/g' \ + -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \ + /tmp/ka$$ +echo '}; +' +sed 's/"//g' /tmp/ka$$ | awk ' +/TNOT/{print "#define KWDOFFSET " NR-1; print ""; + print "STATIC const char *const parsekwd[] = {"} +/TNOT/,/neverfound/{if (last) print " \"" last "\","; last = $3} +END{print " \"" last "\"\n};"}' + +rm /tmp/ka$$ diff --git a/usr/dash/myhistedit.h b/usr/dash/myhistedit.h new file mode 100644 index 0000000..5888088 --- /dev/null +++ b/usr/dash/myhistedit.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. 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. + * 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. + * + * @(#)myhistedit.h 8.2 (Berkeley) 5/4/95 + */ + +#include <histedit.h> + +extern History *hist; +extern EditLine *el; +extern int displayhist; + +void histedit(void); +void sethistsize(const char *); +void setterm(const char *); +int histcmd(int, char **); +int not_fcnumber(char *); +int str_to_event(const char *, int); diff --git a/usr/dash/mystring.c b/usr/dash/mystring.c new file mode 100644 index 0000000..0106bd2 --- /dev/null +++ b/usr/dash/mystring.c @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +/* + * String functions. + * + * equal(s1, s2) Return true if strings are equal. + * scopy(from, to) Copy a string. + * scopyn(from, to, n) Like scopy, but checks for overflow. + * number(s) Convert a string of digits to an integer. + * is_number(s) Return true if s is a string of digits. + */ + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <inttypes.h> +#include <stdlib.h> +#include "shell.h" +#include "syntax.h" +#include "error.h" +#include "mystring.h" +#include "memalloc.h" +#include "parser.h" +#include "system.h" + + +char nullstr[1]; /* zero length string */ +const char spcstr[] = " "; +const char snlfmt[] = "%s\n"; +const char dolatstr[] = { CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', + CTLQUOTEMARK, '\0' }; +const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; +const char illnum[] = "Illegal number: %s"; +const char homestr[] = "HOME"; + +/* + * equal - #defined in mystring.h + */ + +/* + * scopy - #defined in mystring.h + */ + + +#if 0 +/* + * scopyn - copy a string from "from" to "to", truncating the string + * if necessary. "To" is always nul terminated, even if + * truncation is performed. "Size" is the size of "to". + */ + +void +scopyn(const char *from, char *to, int size) +{ + + while (--size > 0) { + if ((*to++ = *from++) == '\0') + return; + } + *to = '\0'; +} +#endif + + +/* + * prefix -- see if pfx is a prefix of string. + */ + +char * +prefix(const char *string, const char *pfx) +{ + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return (char *) string; +} + +void badnum(const char *s) +{ + sh_error(illnum, s); +} + +/* + * Convert a string into an integer of type intmax_t. Alow trailing spaces. + */ +intmax_t atomax(const char *s, int base) +{ + char *p; + intmax_t r; + + errno = 0; + r = strtoimax(s, &p, base); + + if (errno != 0) + badnum(s); + + /* + * Disallow completely blank strings in non-arithmetic (base != 0) + * contexts. + */ + if (p == s && base) + badnum(s); + + while (isspace((unsigned char)*p)) + p++; + + if (*p) + badnum(s); + + return r; +} + +intmax_t atomax10(const char *s) +{ + return atomax(s, 10); +} + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +int +number(const char *s) +{ + intmax_t n = atomax10(s); + + if (n < 0 || n > INT_MAX) + badnum(s); + + return n; +} + + + +/* + * Check for a valid number. This should be elsewhere. + */ + +int +is_number(const char *p) +{ + do { + if (! is_digit(*p)) + return 0; + } while (*++p != '\0'); + return 1; +} + + +/* + * Produce a possibly single quoted string suitable as input to the shell. + * The return string is allocated on the stack. + */ + +char * +single_quote(const char *s) { + char *p; + + STARTSTACKSTR(p); + + do { + char *q; + size_t len; + + len = strchrnul(s, '\'') - s; + + q = p = makestrspace(len + 3, p); + + *q++ = '\''; + q = mempcpy(q, s, len); + *q++ = '\''; + s += len; + + STADJUST(q - p, p); + + len = strspn(s, "'"); + if (!len) + break; + + q = p = makestrspace(len + 3, p); + + *q++ = '"'; + q = mempcpy(q, s, len); + *q++ = '"'; + s += len; + + STADJUST(q - p, p); + } while (*s); + + USTPUTC(0, p); + + return stackblock(); +} + +/* + * Like strdup but works with the ash stack. + */ + +char * +sstrdup(const char *p) +{ + size_t len = strlen(p) + 1; + return memcpy(stalloc(len), p, len); +} + +/* + * Wrapper around strcmp for qsort/bsearch/... + */ +int +pstrcmp(const void *a, const void *b) +{ + return strcmp(*(const char *const *) a, *(const char *const *) b); +} + +/* + * Find a string is in a sorted array. + */ +const char *const * +findstring(const char *s, const char *const *array, size_t nmemb) +{ + return bsearch(&s, array, nmemb, sizeof(const char *), pstrcmp); +} diff --git a/usr/dash/mystring.h b/usr/dash/mystring.h new file mode 100644 index 0000000..083ea98 --- /dev/null +++ b/usr/dash/mystring.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)mystring.h 8.2 (Berkeley) 5/4/95 + */ + +#include <inttypes.h> +#include <string.h> + +extern const char snlfmt[]; +extern const char spcstr[]; +extern const char dolatstr[]; +#define DOLATSTRLEN 6 +extern const char qchars[]; +extern const char illnum[]; +extern const char homestr[]; + +#if 0 +void scopyn(const char *, char *, int); +#endif +char *prefix(const char *, const char *); +void badnum(const char *s) __attribute__ ((noreturn)); +intmax_t atomax(const char *, int); +intmax_t atomax10(const char *); +int number(const char *); +int is_number(const char *); +char *single_quote(const char *); +char *sstrdup(const char *); +int pstrcmp(const void *, const void *); +const char *const *findstring(const char *, const char *const *, size_t); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) +#define scopy(s1, s2) ((void)strcpy(s2, s1)) diff --git a/usr/dash/nodes.c.pat b/usr/dash/nodes.c.pat new file mode 100644 index 0000000..9125bc7 --- /dev/null +++ b/usr/dash/nodes.c.pat @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 + */ + +#include <stdlib.h> +/* + * Routine for dealing with parsed shell commands. + */ + +#include "shell.h" +#include "nodes.h" +#include "memalloc.h" +#include "machdep.h" +#include "mystring.h" +#include "system.h" + + +int funcblocksize; /* size of structures in function */ +int funcstringsize; /* size of strings in node */ +pointer funcblock; /* block to allocate function from */ +char *funcstring; /* block to allocate strings from */ + +%SIZES + + +STATIC void calcsize(union node *); +STATIC void sizenodelist(struct nodelist *); +STATIC union node *copynode(union node *); +STATIC struct nodelist *copynodelist(struct nodelist *); +STATIC char *nodesavestr(char *); + + + +/* + * Make a copy of a parse tree. + */ + +struct funcnode * +copyfunc(union node *n) +{ + struct funcnode *f; + size_t blocksize; + + funcblocksize = offsetof(struct funcnode, n); + funcstringsize = 0; + calcsize(n); + blocksize = funcblocksize; + f = ckmalloc(blocksize + funcstringsize); + funcblock = (char *) f + offsetof(struct funcnode, n); + funcstring = (char *) f + blocksize; + copynode(n); + f->count = 0; + return f; +} + + + +STATIC void +calcsize(n) + union node *n; +{ + %CALCSIZE +} + + + +STATIC void +sizenodelist(lp) + struct nodelist *lp; +{ + while (lp) { + funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + + +STATIC union node * +copynode(n) + union node *n; +{ + union node *new; + + %COPY + return new; +} + + +STATIC struct nodelist * +copynodelist(lp) + struct nodelist *lp; +{ + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock = (char *) funcblock + + SHELL_ALIGN(sizeof(struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + + +STATIC char * +nodesavestr(s) + char *s; +{ + char *rtn = funcstring; + + funcstring = stpcpy(funcstring, s) + 1; + return rtn; +} + + + +/* + * Free a parse tree. + */ + +void +freefunc(struct funcnode *f) +{ + if (f && --f->count < 0) + ckfree(f); +} diff --git a/usr/dash/nodetypes b/usr/dash/nodetypes new file mode 100644 index 0000000..ceaf478 --- /dev/null +++ b/usr/dash/nodetypes @@ -0,0 +1,151 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# Copyright (c) 1997-2005 +# Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# 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. +# +# @(#)nodetypes 8.2 (Berkeley) 5/4/95 + +# This file describes the nodes used in parse trees. Unindented lines +# contain a node type followed by a structure tag. Subsequent indented +# lines specify the fields of the structure. Several node types can share +# the same structure, in which case the fields of the structure should be +# specified only once. +# +# A field of a structure is described by the name of the field followed +# by a type. The currently implemented types are: +# nodeptr - a pointer to a node +# nodelist - a pointer to a list of nodes +# string - a pointer to a nul terminated string +# int - an integer +# other - any type that can be copied by assignment +# temp - a field that doesn't have to be copied when the node is copied +# The last two types should be followed by the text of a C declaration for +# the field. + +NCMD ncmd # a simple command + type int + linno int + assign nodeptr # variable assignments + args nodeptr # the arguments + redirect nodeptr # list of file redirections + +NPIPE npipe # a pipeline + type int + backgnd int # set to run pipeline in background + cmdlist nodelist # the commands in the pipeline + +NREDIR nredir # redirection (of a complex command) + type int + linno int + n nodeptr # the command + redirect nodeptr # list of file redirections + +NBACKGND nredir # run command in background +NSUBSHELL nredir # run command in a subshell + +NAND nbinary # the && operator +NOR nbinary # the || operator + +NSEMI nbinary # two commands separated by a semicolon + type int + ch1 nodeptr # the first child + ch2 nodeptr # the second child + +NIF nif # the if statement. Elif clauses are handled + type int # using multiple if nodes. + test nodeptr # if test + ifpart nodeptr # then ifpart + elsepart nodeptr # else elsepart + +NWHILE nbinary # the while statement. First child is the test +NUNTIL nbinary # the until statement + +NFOR nfor # the for statement + type int + linno int + args nodeptr # for var in args + body nodeptr # do body; done + var string # the for variable + +NCASE ncase # a case statement + type int + linno int + expr nodeptr # the word to switch on + cases nodeptr # the list of cases (NCLIST nodes) + +NCLIST nclist # a case + type int + next nodeptr # the next case in list + pattern nodeptr # list of patterns for this case + body nodeptr # code to execute for this case + + +NDEFUN ndefun # a function + type int + linno int + text string + body nodeptr + +NARG narg # represents a word + type int + next nodeptr # next word in list + text string # the text of the word + backquote nodelist # list of commands in back quotes + +NTO nfile # fd> fname +NCLOBBER nfile # fd>| fname +NFROM nfile # fd< fname +NFROMTO nfile # fd<> fname +NAPPEND nfile # fd>> fname + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + fname nodeptr # file name, in a NARG node + expfname temp char *expfname # actual file name + +NTOFD ndup # fd<&dupfd +NFROMFD ndup # fd>&dupfd + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + dupfd int # file descriptor to duplicate + vname nodeptr # file name if fd>&$var + + +NHERE nhere # fd<<\! +NXHERE nhere # fd<<! + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + doc nodeptr # input to command (NARG node) + +NNOT nnot # ! command (actually pipeline) + type int + com nodeptr diff --git a/usr/dash/options.c b/usr/dash/options.c new file mode 100644 index 0000000..6f381e6 --- /dev/null +++ b/usr/dash/options.c @@ -0,0 +1,559 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> + +#include "shell.h" +#define DEFINE_OPTIONS +#include "options.h" +#undef DEFINE_OPTIONS +#include "nodes.h" /* for other header files */ +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#ifndef SMALL +#include "myhistedit.h" +#endif +#include "show.h" + +char *arg0; /* value of $0 */ +struct shparam shellparam; /* current positional parameters */ +char **argptr; /* argument list for builtin commands */ +char *optionarg; /* set by nextopt (like getopt) */ +char *optptr; /* used by nextopt */ + +char *minusc; /* argument to -c option */ + +static const char *const optnames[NOPTS] = { + "errexit", + "noglob", + "ignoreeof", + "interactive", + "monitor", + "noexec", + "stdin", + "xtrace", + "verbose", + "vi", + "emacs", + "noclobber", + "allexport", + "notify", + "nounset", + "nolog", + "debug", +}; + +const char optletters[NOPTS] = { + 'e', + 'f', + 'I', + 'i', + 'm', + 'n', + 's', + 'x', + 'v', + 'V', + 'E', + 'C', + 'a', + 'b', + 'u', + 0, + 0, +}; + +char optlist[NOPTS]; + + +static int options(int); +STATIC void minus_o(char *, int); +STATIC void setoption(int, int); +STATIC int getopts(char *, char *, char **); + + +/* + * Process the shell command line arguments. + */ + +int +procargs(int argc, char **argv) +{ + int i; + const char *xminusc; + char **xargv; + int login; + + xargv = argv; + login = xargv[0] && xargv[0][0] == '-'; + arg0 = xargv[0]; + if (argc > 0) + xargv++; + for (i = 0; i < NOPTS; i++) + optlist[i] = 2; + argptr = xargv; + login |= options(1); + xargv = argptr; + xminusc = minusc; + if (*xargv == NULL) { + if (xminusc) + sh_error("-c requires an argument"); + sflag = 1; + } + if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) + iflag = 1; + if (mflag == 2) + mflag = iflag; + for (i = 0; i < NOPTS; i++) + if (optlist[i] == 2) + optlist[i] = 0; +#if DEBUG == 2 + debug = 1; +#endif + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (xminusc) { + minusc = *xargv++; + if (*xargv) + goto setarg0; + } else if (!sflag) { + setinputfile(*xargv, 0); +setarg0: + arg0 = *xargv++; + commandname = arg0; + } + + shellparam.p = xargv; + shellparam.optind = 1; + shellparam.optoff = -1; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*xargv) { + shellparam.nparam++; + xargv++; + } + optschanged(); + + return login; +} + + +void +optschanged(void) +{ +#ifdef DEBUG + opentrace(); +#endif + setinteractive(iflag); +#ifndef SMALL + histedit(); +#endif + setjobctl(mflag); +} + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +STATIC int +options(int cmdline) +{ + char *p; + int val; + int c; + int login = 0; + + if (cmdline) + minusc = NULL; + while ((p = *argptr) != NULL) { + argptr++; + if ((c = *p++) == '-') { + val = 1; + if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { + if (!cmdline) { + /* "-" means turn off -x and -v */ + if (p[0] == '\0') + xflag = vflag = 0; + /* "--" means reset params */ + else if (*argptr == NULL) + setparam(argptr); + } + break; /* "-" or "--" terminates options */ + } + } else if (c == '+') { + val = 0; + } else { + argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + minusc = p; /* command is after shell args*/ + } else if (c == 'l' && cmdline) { + login = 1; + } else if (c == 'o') { + minus_o(*argptr, val); + if (*argptr) + argptr++; + } else { + setoption(c, val); + } + } + } + + return login; +} + +STATIC void +minus_o(char *name, int val) +{ + int i; + + if (name == NULL) { + if (val) { + out1str("Current option settings\n"); + for (i = 0; i < NOPTS; i++) + out1fmt("%-16s%s\n", optnames[i], + optlist[i] ? "on" : "off"); + } else { + for (i = 0; i < NOPTS; i++) + out1fmt("set %s %s\n", + optlist[i] ? "-o" : "+o", + optnames[i]); + + } + } else { + for (i = 0; i < NOPTS; i++) + if (equal(name, optnames[i])) { + optlist[i] = val; + return; + } + sh_error("Illegal option -o %s", name); + } +} + + +STATIC void +setoption(int flag, int val) +{ + int i; + + for (i = 0; i < NOPTS; i++) + if (optletters[i] == flag) { + optlist[i] = val; + if (val) { + /* #%$ hack for ksh semantics */ + if (flag == 'V') + Eflag = 0; + else if (flag == 'E') + Vflag = 0; + } + return; + } + sh_error("Illegal option -%c", flag); + /* NOTREACHED */ +} + + + +/* + * Set the shell parameters. + */ + +void +setparam(char **argv) +{ + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; + shellparam.optind = 1; + shellparam.optoff = -1; +} + + +/* + * Free the list of positional parameters. + */ + +void +freeparam(volatile struct shparam *param) +{ + char **ap; + + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(*ap); + ckfree(param->p); + } +} + + + +/* + * The shift builtin command. + */ + +int +shiftcmd(int argc, char **argv) +{ + int n; + char **ap1, **ap2; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > shellparam.nparam) + sh_error("can't shift that many"); + INTOFF; + shellparam.nparam -= n; + for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { + if (shellparam.malloc) + ckfree(*ap1); + } + ap2 = shellparam.p; + while ((*ap2++ = *ap1++) != NULL); + shellparam.optind = 1; + shellparam.optoff = -1; + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +int +setcmd(int argc, char **argv) +{ + if (argc == 1) + return showvars(nullstr, 0, VUNSET); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +void +getoptsreset(value) + const char *value; +{ + shellparam.optind = number(value) ?: 1; + shellparam.optoff = -1; +} + +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +int +getoptscmd(int argc, char **argv) +{ + char **optbase; + + if (argc < 3) + sh_error("Usage: getopts optstring var [arg]"); + else if (argc == 3) { + optbase = shellparam.p; + if ((unsigned)shellparam.optind > shellparam.nparam + 1) { + shellparam.optind = 1; + shellparam.optoff = -1; + } + } + else { + optbase = &argv[3]; + if ((unsigned)shellparam.optind > argc - 2) { + shellparam.optind = 1; + shellparam.optoff = -1; + } + } + + return getopts(argv[1], argv[2], optbase); +} + +STATIC int +getopts(char *optstr, char *optvar, char **optfirst) +{ + char *p, *q; + char c = '?'; + int done = 0; + char s[2]; + char **optnext; + int ind = shellparam.optind; + int off = shellparam.optoff; + + shellparam.optind = -1; + optnext = optfirst + ind - 1; + + if (ind <= 1 || off < 0 || strlen(optnext[-1]) < off) + p = NULL; + else + p = optnext[-1] + off; + if (p == NULL || *p == '\0') { + /* Current word is done, advance */ + p = *optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + p = NULL; + done = 1; + goto out; + } + optnext++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + + c = *p++; + for (q = optstr; *q != c; ) { + if (*q == '\0') { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + setvar("OPTARG", s, 0); + } else { + outfmt(&errout, "Illegal option -%c\n", c); + (void) unsetvar("OPTARG"); + } + c = '?'; + goto out; + } + if (*++q == ':') + q++; + } + + if (*++q == ':') { + if (*p == '\0' && (p = *optnext) == NULL) { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + setvar("OPTARG", s, 0); + c = ':'; + } else { + outfmt(&errout, "No arg for -%c option\n", c); + (void) unsetvar("OPTARG"); + c = '?'; + } + goto out; + } + + if (p == *optnext) + optnext++; + setvar("OPTARG", p, 0); + p = NULL; + } else + setvar("OPTARG", nullstr, 0); + +out: + ind = optnext - optfirst + 1; + setvarint("OPTIND", ind, VNOFUNC); + s[0] = c; + s[1] = '\0'; + setvar(optvar, s, 0); + + shellparam.optoff = p ? p - *(optnext - 1) : -1; + shellparam.optind = ind; + + return done; +} + +/* + * XXX - should get rid of. have all builtins use getopt(3). the + * library getopt must have the BSD extension static variable "optreset" + * otherwise it can't be used within the shell safely. + * + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It return the character, or '\0' on + * end of input. + */ + +int +nextopt(const char *optstring) +{ + char *p; + const char *q; + char c; + + if ((p = optptr) == NULL || *p == '\0') { + p = *argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + sh_error("Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *argptr++) == NULL) + sh_error("No arg for -%c option", c); + optionarg = p; + p = NULL; + } + optptr = p; + return c; +} diff --git a/usr/dash/options.h b/usr/dash/options.h new file mode 100644 index 0000000..975fe33 --- /dev/null +++ b/usr/dash/options.h @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)options.h 8.2 (Berkeley) 5/4/95 + */ + +struct shparam { + int nparam; /* # of positional parameters (without $0) */ + unsigned char malloc; /* if parameter list dynamically allocated */ + char **p; /* parameter list */ + int optind; /* next parameter to be processed by getopts */ + int optoff; /* used by getopts */ +}; + + + +#define eflag optlist[0] +#define fflag optlist[1] +#define Iflag optlist[2] +#define iflag optlist[3] +#define mflag optlist[4] +#define nflag optlist[5] +#define sflag optlist[6] +#define xflag optlist[7] +#define vflag optlist[8] +#define Vflag optlist[9] +#define Eflag optlist[10] +#define Cflag optlist[11] +#define aflag optlist[12] +#define bflag optlist[13] +#define uflag optlist[14] +#define nolog optlist[15] +#define debug optlist[16] + +#define NOPTS 17 + +extern const char optletters[NOPTS]; +extern char optlist[NOPTS]; + + +extern char *minusc; /* argument to -c option */ +extern char *arg0; /* $0 */ +extern struct shparam shellparam; /* $@ */ +extern char **argptr; /* argument list for builtin commands */ +extern char *optionarg; /* set by nextopt */ +extern char *optptr; /* used by nextopt */ + +int procargs(int, char **); +void optschanged(void); +void setparam(char **); +void freeparam(volatile struct shparam *); +int shiftcmd(int, char **); +int setcmd(int, char **); +int getoptscmd(int, char **); +int nextopt(const char *); +void getoptsreset(const char *); diff --git a/usr/dash/output.c b/usr/dash/output.c new file mode 100644 index 0000000..bb7c6ad --- /dev/null +++ b/usr/dash/output.c @@ -0,0 +1,399 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +/* + * Shell output routines. We use our own output routines because: + * When a builtin command is interrupted we have to discard + * any pending output. + * When a builtin command appears in back quotes, we want to + * save the output of the command in a region obtained + * via malloc, rather than doing a fork and reading the + * output of the command via a pipe. + * Our output routines may be smaller than the stdio routines. + */ + +#include <sys/types.h> /* quad_t */ +#include <sys/param.h> /* BSD4_4 */ +#include <sys/ioctl.h> + +#include <stdio.h> /* defines BUFSIZ */ +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#ifdef USE_GLIBC_STDIO +#include <fcntl.h> +#endif +#include <limits.h> + +#include "shell.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "main.h" +#include "system.h" + + +#define OUTBUFSIZ BUFSIZ +#define MEM_OUT -3 /* output to dynamically allocated memory */ + + +#ifdef USE_GLIBC_STDIO +struct output output = { + .stream = 0, .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = 1, .flags = 0 +}; +struct output errout = { + .stream = 0, .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = 2, .flags = 0 +} +#ifdef notyet +struct output memout = { + .stream = 0, .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = MEM_OUT, .flags = 0 +}; +#endif +#else +struct output output = { + .nextc = 0, .end = 0, .buf = 0, .bufsize = OUTBUFSIZ, .fd = 1, .flags = 0 +}; +struct output errout = { + .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = 2, .flags = 0 +}; +struct output preverrout; +#ifdef notyet +struct output memout = { + .nextc = 0, .end = 0, .buf = 0, .bufsize = 0, .fd = MEM_OUT, .flags = 0 +}; +#endif +#endif +struct output *out1 = &output; +struct output *out2 = &errout; + + +#ifndef USE_GLIBC_STDIO +static void __outstr(const char *, size_t, struct output *); +#endif +static int xvsnprintf(char *, size_t, const char *, va_list); + + +#ifdef mkinit + +INCLUDE "output.h" +INCLUDE "memalloc.h" + +INIT { +#ifdef USE_GLIBC_STDIO + initstreams(); +#endif +} + +RESET { +#ifdef notyet + out1 = &output; + out2 = &errout; +#ifdef USE_GLIBC_STDIO + if (memout.stream != NULL) + __closememout(); +#endif + if (memout.buf != NULL) { + ckfree(memout.buf); + memout.buf = NULL; + } +#endif +} + +#endif + + +#ifndef USE_GLIBC_STDIO +static void +__outstr(const char *p, size_t len, struct output *dest) +{ + size_t bufsize; + size_t offset; + size_t nleft; + + nleft = dest->end - dest->nextc; + if (nleft >= len) { +buffered: + dest->nextc = mempcpy(dest->nextc, p, len); + return; + } + + bufsize = dest->bufsize; + if (!bufsize) { + ; + } else if (dest->buf == NULL) { + if (dest->fd == MEM_OUT && len > bufsize) { + bufsize = len; + } + offset = 0; + goto alloc; + } else if (dest->fd == MEM_OUT) { + offset = bufsize; + if (bufsize >= len) { + bufsize <<= 1; + } else { + bufsize += len; + } + if (bufsize < offset) + goto err; +alloc: + INTOFF; + dest->buf = ckrealloc(dest->buf, bufsize); + dest->bufsize = bufsize; + dest->end = dest->buf + bufsize; + dest->nextc = dest->buf + offset; + INTON; + } else { + flushout(dest); + } + + nleft = dest->end - dest->nextc; + if (nleft > len) + goto buffered; + + if ((xwrite(dest->fd, p, len))) { +err: + dest->flags |= OUTPUT_ERR; + } +} +#endif + + +void +outstr(const char *p, struct output *file) +{ +#ifdef USE_GLIBC_STDIO + INTOFF; + fputs(p, file->stream); + INTON; +#else + size_t len; + + len = strlen(p); + __outstr(p, len, file); +#endif +} + + +#ifndef USE_GLIBC_STDIO + + +void +outcslow(int c, struct output *dest) +{ + char buf = c; + __outstr(&buf, 1, dest); +} +#endif + + +void +flushall(void) +{ + flushout(&output); +#ifdef FLUSHERR + flushout(&errout); +#endif +} + + +void +flushout(struct output *dest) +{ +#ifdef USE_GLIBC_STDIO + INTOFF; + fflush(dest->stream); + INTON; +#else + size_t len; + + len = dest->nextc - dest->buf; + if (!len || dest->fd < 0) + return; + dest->nextc = dest->buf; + if ((xwrite(dest->fd, dest->buf, len))) + dest->flags |= OUTPUT_ERR; +#endif +} + + +void +outfmt(struct output *file, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doformat(out1, fmt, ap); + va_end(ap); +} + + +int +fmtstr(char *outbuf, size_t length, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = xvsnprintf(outbuf, length, fmt, ap); + va_end(ap); + return ret; +} + + +#ifndef USE_GLIBC_STDIO +void +doformat(struct output *dest, const char *f, va_list ap) +{ + struct stackmark smark; + char *s; + int len, ret; + size_t size; + va_list ap2; + + va_copy(ap2, ap); + size = dest->end - dest->nextc; + len = xvsnprintf(dest->nextc, size, f, ap2); + va_end(ap2); + if (len < 0) { + dest->flags |= OUTPUT_ERR; + return; + } + if (len < size) { + dest->nextc += len; + return; + } + setstackmark(&smark); + s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1); + ret = xvsnprintf(s, len + 1, f, ap); + if (ret == len) + __outstr(s, len, dest); + else + dest->flags |= OUTPUT_ERR; + popstackmark(&smark); +} +#endif + + + +/* + * Version of write which resumes after a signal is caught. + */ + +int +xwrite(int fd, const void *p, size_t n) +{ + const char *buf = p; + + while (n) { + ssize_t i; + size_t m; + + m = n; + if (m > SSIZE_MAX) + m = SSIZE_MAX; + do { + i = write(fd, buf, m); + } while (i < 0 && errno == EINTR); + if (i < 0) + return -1; + buf += i; + n -= i; + } + return 0; +} + + +#ifdef notyet +#ifdef USE_GLIBC_STDIO +void initstreams() { + output.stream = stdout; + errout.stream = stderr; +} + + +void +openmemout(void) { + INTOFF; + memout.stream = open_memstream(&memout.buf, &memout.bufsize); + INTON; +} + + +int +__closememout(void) { + int error; + error = fclose(memout.stream); + memout.stream = NULL; + return error; +} +#endif +#endif + + +static int +xvsnprintf(char *outbuf, size_t length, const char *fmt, va_list ap) +{ + int ret; + +#ifdef __sun + /* + * vsnprintf() on older versions of Solaris returns -1 when + * passed a length of 0. To avoid this, use a dummy + * 1-character buffer instead. + */ + char dummy[1]; + + if (length == 0) { + outbuf = dummy; + length = sizeof(dummy); + } +#endif + + INTOFF; + ret = vsnprintf(outbuf, length, fmt, ap); + INTON; + return ret; +} diff --git a/usr/dash/output.h b/usr/dash/output.h new file mode 100644 index 0000000..f853e9d --- /dev/null +++ b/usr/dash/output.h @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)output.h 8.2 (Berkeley) 5/4/95 + */ + +#ifndef OUTPUT_INCL + +#include <stdarg.h> +#ifdef USE_GLIBC_STDIO +#include <stdio.h> +#endif +#include <sys/types.h> + +struct output { +#ifdef USE_GLIBC_STDIO + FILE *stream; +#endif + char *nextc; + char *end; + char *buf; + size_t bufsize; + int fd; + int flags; +}; + +extern struct output output; +extern struct output errout; +extern struct output preverrout; +#ifdef notyet +extern struct output memout; +#endif +extern struct output *out1; +extern struct output *out2; + +void outstr(const char *, struct output *); +#ifndef USE_GLIBC_STDIO +void outcslow(int, struct output *); +#endif +void flushall(void); +void flushout(struct output *); +void outfmt(struct output *, const char *, ...) + __attribute__((__format__(__printf__,2,3))); +void out1fmt(const char *, ...) + __attribute__((__format__(__printf__,1,2))); +int fmtstr(char *, size_t, const char *, ...) + __attribute__((__format__(__printf__,3,4))); +#ifndef USE_GLIBC_STDIO +void doformat(struct output *, const char *, va_list); +#endif +int xwrite(int, const void *, size_t); +#ifdef notyet +#ifdef USE_GLIBC_STDIO +void initstreams(void); +void openmemout(void); +int __closememout(void); +#endif +#endif + +static inline void +freestdout() +{ + output.nextc = output.buf; + output.flags = 0; +} + +#define OUTPUT_ERR 01 /* error occurred on output */ + +#ifdef USE_GLIBC_STDIO +static inline void outc(int ch, struct output *file) +{ + putc(ch, file->stream); +} +#define doformat(d, f, a) vfprintf((d)->stream, (f), (a)) +#else +static inline void outc(int ch, struct output *file) +{ + if (file->nextc == file->end) + outcslow(ch, file); + else { + *file->nextc = ch; + file->nextc++; + } +} +#endif +#define out1c(c) outc((c), out1) +#define out2c(c) outcslow((c), out2) +#define out1str(s) outstr((s), out1) +#define out2str(s) outstr((s), out2) +#define outerr(f) (f)->flags + +#define OUTPUT_INCL +#endif diff --git a/usr/dash/parser.c b/usr/dash/parser.c new file mode 100644 index 0000000..6e076a5 --- /dev/null +++ b/usr/dash/parser.c @@ -0,0 +1,1568 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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 HAVE_ALLOCA_H +#include <alloca.h> +#endif + +#include <stdlib.h> + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" /* defines rmescapes() */ +#include "exec.h" /* defines find_builtin() */ +#include "syntax.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "var.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "show.h" +#include "builtins.h" +#include "system.h" +#ifndef SMALL +#include "myhistedit.h" +#endif + +/* + * Shell command parser. + */ + +/* values returned by readtoken */ +#include "token.h" + + + +/* Used by expandstr to get here-doc like behaviour. */ +#define FAKEEOFMARK (char *)1 + + + +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + int striptabs; /* if set, strip leading tabs */ +}; + + + +struct heredoc *heredoclist; /* list of here documents to read */ +int doprompt; /* if set, prompt the user */ +int needprompt; /* true if interactive and at start of line */ +int lasttoken; /* last token read */ +MKINIT int tokpushback; /* last token pushed back */ +char *wordtext; /* text of last word returned by readtoken */ +int checkkwd; +struct nodelist *backquotelist; +union node *redirnode; +struct heredoc *heredoc; +int quoteflag; /* set if (part of) last token was quoted */ + + +STATIC union node *list(int); +STATIC union node *andor(void); +STATIC union node *pipeline(void); +STATIC union node *command(void); +STATIC union node *simplecmd(void); +STATIC union node *makename(void); +STATIC void parsefname(void); +STATIC void parseheredoc(void); +STATIC int peektoken(void); +STATIC int readtoken(void); +STATIC int xxreadtoken(void); +STATIC int readtoken1(int, char const *, char *, int); +STATIC void synexpect(int) __attribute__((__noreturn__)); +STATIC void synerror(const char *) __attribute__((__noreturn__)); +STATIC void setprompt(int); + + +static inline int +isassignment(const char *p) +{ + const char *q = endofname(p); + if (p == q) + return 0; + return *q == '='; +} + +static inline int realeofmark(const char *eofmark) +{ + return eofmark && eofmark != FAKEEOFMARK; +} + + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +union node * +parsecmd(int interact) +{ + int t; + + tokpushback = 0; + doprompt = interact; + if (doprompt) + setprompt(doprompt); + needprompt = 0; + t = readtoken(); + if (t == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +STATIC union node * +list(int nlflag) +{ + union node *n1, *n2, *n3; + int tok; + + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (nlflag == 2 && tokendlist[peektoken()]) + return NULL; + n1 = NULL; + for (;;) { + n2 = andor(); + tok = readtoken(); + if (tok == TBACKGND) { + if (n2->type == NPIPE) { + n2->npipe.backgnd = 1; + } else { + if (n2->type != NREDIR) { + n3 = stalloc(sizeof(struct nredir)); + n3->nredir.n = n2; + n3->nredir.redirect = NULL; + n2 = n3; + } + n2->type = NBACKGND; + } + } + if (n1 == NULL) { + n1 = n2; + } + else { + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = NSEMI; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } + switch (tok) { + case TBACKGND: + case TSEMI: + tok = readtoken(); + /* fall through */ + case TNL: + if (tok == TNL) { + parseheredoc(); + if (nlflag == 1) + return n1; + } else { + tokpushback++; + } + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (tokendlist[peektoken()]) + return n1; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return n1; + default: + if (nlflag == 1) + synexpect(-1); + tokpushback++; + return n1; + } + } +} + + + +STATIC union node * +andor(void) +{ + union node *n1, *n2, *n3; + int t; + + n1 = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n1; + } + checkkwd = CHKNL | CHKKWD | CHKALIAS; + n2 = pipeline(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = t; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } +} + + + +STATIC union node * +pipeline(void) +{ + union node *n1, *n2, *pipenode; + struct nodelist *lp, *prev; + int negate; + + negate = 0; + TRACE(("pipeline: entered\n")); + if (readtoken() == TNOT) { + negate = !negate; + checkkwd = CHKKWD | CHKALIAS; + } else + tokpushback++; + n1 = command(); + if (readtoken() == TPIPE) { + pipenode = (union node *)stalloc(sizeof (struct npipe)); + pipenode->type = NPIPE; + pipenode->npipe.backgnd = 0; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + pipenode->npipe.cmdlist = lp; + lp->n = n1; + do { + prev = lp; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + checkkwd = CHKNL | CHKKWD | CHKALIAS; + lp->n = command(); + prev->next = lp; + } while (readtoken() == TPIPE); + lp->next = NULL; + n1 = pipenode; + } + tokpushback++; + if (negate) { + n2 = (union node *)stalloc(sizeof (struct nnot)); + n2->type = NNOT; + n2->nnot.com = n1; + return n2; + } else + return n1; +} + + + +STATIC union node * +command(void) +{ + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + union node **rpp2; + int t; + int savelinno; + + redir = NULL; + rpp2 = &redir; + + savelinno = plinno; + + switch (readtoken()) { + default: + synexpect(-1); + /* NOTREACHED */ + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + n1->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + n2->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + t = TFI; + break; + case TWHILE: + case TUNTIL: { + int got; + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.ch1 = list(0); + if ((got=readtoken()) != TDO) { +TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); + synexpect(TDO); + } + n1->nbinary.ch2 = list(0); + t = TDONE; + break; + } + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.linno = savelinno; + n1->nfor.var = wordtext; + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (readtoken() == TIN) { + app = ≈ + while (readtoken() == TWORD) { + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + *app = n2; + app = &n2->narg.next; + } + *app = NULL; + n1->nfor.args = ap; + if (lasttoken != TNL && lasttoken != TSEMI) + synexpect(-1); + } else { + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = (char *)dolatstr; + n2->narg.backquote = NULL; + n2->narg.next = NULL; + n1->nfor.args = n2; + /* + * Newline or semicolon here is optional (but note + * that the original Bourne shell only allowed NL). + */ + if (lasttoken != TSEMI) + tokpushback++; + } + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (readtoken() != TDO) + synexpect(TDO); + n1->nfor.body = list(0); + t = TDONE; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + n1->ncase.linno = savelinno; + if (readtoken() != TWORD) + synexpect(TWORD); + n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + n2->narg.next = NULL; + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (readtoken() != TIN) + synexpect(TIN); + cpp = &n1->ncase.cases; +next_case: + checkkwd = CHKNL | CHKKWD; + t = readtoken(); + while(t != TESAC) { + if (lasttoken == TLP) + readtoken(); + *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); + cp->type = NCLIST; + app = &cp->nclist.pattern; + for (;;) { + *app = ap = (union node *)stalloc(sizeof (struct narg)); + ap->type = NARG; + ap->narg.text = wordtext; + ap->narg.backquote = backquotelist; + if (readtoken() != TPIPE) + break; + app = &ap->narg.next; + readtoken(); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + synexpect(TRP); + cp->nclist.body = list(2); + + cpp = &cp->nclist.next; + + checkkwd = CHKNL | CHKKWD; + if ((t = readtoken()) != TESAC) { + if (t != TENDCASE) + synexpect(TENDCASE); + else + goto next_case; + } + } + *cpp = NULL; + goto redir; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.linno = savelinno; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + t = TRP; + break; + case TBEGIN: + n1 = list(0); + t = TEND; + break; + case TWORD: + case TREDIR: + tokpushback++; + return simplecmd(); + } + + if (readtoken() != t) + synexpect(t); + +redir: + /* Now check for redirection which may follow command */ + checkkwd = CHKKWD | CHKALIAS; + rpp = rpp2; + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (n1->type != NSUBSHELL) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.linno = savelinno; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + + return n1; +} + + +STATIC union node * +simplecmd(void) { + union node *args, **app; + union node *n = NULL; + union node *vars, **vpp; + union node **rpp, *redir; + int savecheckkwd; + int savelinno; + + args = NULL; + app = &args; + vars = NULL; + vpp = &vars; + redir = NULL; + rpp = &redir; + + savecheckkwd = CHKALIAS; + savelinno = plinno; + for (;;) { + checkkwd = savecheckkwd; + switch (readtoken()) { + case TWORD: + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + if (savecheckkwd && isassignment(wordtext)) { + *vpp = n; + vpp = &n->narg.next; + } else { + *app = n; + app = &n->narg.next; + savecheckkwd = 0; + } + break; + case TREDIR: + *rpp = n = redirnode; + rpp = &n->nfile.next; + parsefname(); /* read name of redirection file */ + break; + case TLP: + if ( + args && app == &args->narg.next && + !vars && !redir + ) { + struct builtincmd *bcmd; + const char *name; + + /* We have a function */ + if (readtoken() != TRP) + synexpect(TRP); + name = n->narg.text; + if ( + !goodname(name) || ( + (bcmd = find_builtin(name)) && + bcmd->flags & BUILTIN_SPECIAL + ) + ) + synerror("Bad function name"); + n->type = NDEFUN; + checkkwd = CHKNL | CHKKWD | CHKALIAS; + n->ndefun.text = n->narg.text; + n->ndefun.linno = plinno; + n->ndefun.body = command(); + return n; + } + /* fall through */ + default: + tokpushback++; + goto out; + } + } +out: + *app = NULL; + *vpp = NULL; + *rpp = NULL; + n = (union node *)stalloc(sizeof (struct ncmd)); + n->type = NCMD; + n->ncmd.linno = savelinno; + n->ncmd.args = args; + n->ncmd.assign = vars; + n->ncmd.redirect = redir; + return n; +} + +STATIC union node * +makename(void) +{ + union node *n; + + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + return n; +} + +void fixredir(union node *n, const char *text, int err) + { + TRACE(("Fix redir %s %d\n", text, err)); + if (!err) + n->ndup.vname = NULL; + + if (is_digit(text[0]) && text[1] == '\0') + n->ndup.dupfd = digit_val(text[0]); + else if (text[0] == '-' && text[1] == '\0') + n->ndup.dupfd = -1; + else { + + if (err) + synerror("Bad fd number"); + else + n->ndup.vname = makename(); + } +} + + +STATIC void +parsefname(void) +{ + union node *n = redirnode; + + if (n->type == NHERE) + checkkwd = CHKEOFMARK; + if (readtoken() != TWORD) + synexpect(-1); + if (n->type == NHERE) { + struct heredoc *here = heredoc; + struct heredoc *p; + + if (quoteflag == 0) + n->type = NXHERE; + TRACE(("Here document %d\n", n->type)); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + fixredir(n, wordtext, 0); + } else { + n->nfile.fname = makename(); + } +} + + +/* + * Input any here documents. + */ + +STATIC void +parseheredoc(void) +{ + struct heredoc *here; + union node *n; + + here = heredoclist; + heredoclist = 0; + + while (here) { + if (needprompt) { + setprompt(2); + } + readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, + here->eofmark, here->striptabs); + n = (union node *)stalloc(sizeof (struct narg)); + n->narg.type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + here->here->nhere.doc = n; + here = here->next; + } +} + +STATIC int +peektoken(void) +{ + int t; + + t = readtoken(); + tokpushback++; + return (t); +} + +STATIC int +readtoken(void) +{ + int t; + int kwd = checkkwd; +#ifdef DEBUG + int alreadyseen = tokpushback; +#endif + +top: + t = xxreadtoken(); + + /* + * eat newlines + */ + if (kwd & CHKNL) { + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } + + if (t != TWORD || quoteflag) { + goto out; + } + + /* + * check for keywords + */ + if (kwd & CHKKWD) { + const char *const *pp; + + if ((pp = findkwd(wordtext))) { + lasttoken = t = pp - parsekwd + KWDOFFSET; + TRACE(("keyword %s recognized\n", tokname[t])); + goto out; + } + } + + if (checkkwd & CHKALIAS) { + struct alias *ap; + if ((ap = lookupalias(wordtext, 1)) != NULL) { + if (*ap->val) { + pushstring(ap->val, ap); + } + goto top; + } + } +out: + checkkwd = 0; +#ifdef DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define RETURN(token) return lasttoken = token + +STATIC int +xxreadtoken(void) +{ + int c; + + if (tokpushback) { + tokpushback = 0; + return lasttoken; + } + if (needprompt) { + setprompt(2); + } + for (;;) { /* until token or start of word found */ + c = pgetc_macro(); + switch (c) { + case ' ': case '\t': + case PEOA: + continue; + case '#': + while ((c = pgetc()) != '\n' && c != PEOF); + pungetc(); + continue; + case '\\': + if (pgetc() == '\n') { + plinno++; + if (doprompt) + setprompt(2); + continue; + } + pungetc(); + goto breakloop; + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + if (pgetc() == ';') + RETURN(TENDCASE); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + default: + goto breakloop; + } + } +breakloop: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); +#undef RETURN +} + + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEARITH() {goto parsearith; parsearith_return:;} + +STATIC int +readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) +{ + int c = firstc; + char *out; + size_t len; + struct nodelist *bqlist; + int quotef; + int dblquote; + int varnest; /* levels of variables expansion */ + int arinest; /* levels of arithmetic expansion */ + int parenlevel; /* levels of parens in arithmetic */ + int dqvarnest; /* levels of variables expansion within double quotes */ + int oldstyle; + /* syntax before arithmetic */ + char const *uninitialized_var(prevsyntax); + + dblquote = 0; + if (syntax == DQSYNTAX) + dblquote = 1; + quotef = 0; + bqlist = NULL; + varnest = 0; + arinest = 0; + parenlevel = 0; + dqvarnest = 0; + + STARTSTACKSTR(out); + loop: { /* for each line, until end of word */ +#if ATTY + if (c == '\034' && doprompt + && attyset() && ! equal(termval(), "emacs")) { + attyline(); + if (syntax == BASESYNTAX) + return readtoken(); + c = pgetc(); + goto loop; + } +#endif + CHECKEND(); /* set c to PEOF if at end of here document */ + for (;;) { /* until end of line or end of word */ + CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ + switch(syntax[c]) { + case CNL: /* '\n' */ + if (syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) + setprompt(2); + c = pgetc(); + goto loop; /* continue outer loop */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if (eofmark == NULL || dblquote) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + /* backslash */ + case CBACK: + c = pgetc2(); + if (c == PEOF) { + USTPUTC(CTLESC, out); + USTPUTC('\\', out); + pungetc(); + } else if (c == '\n') { + plinno++; + if (doprompt) + setprompt(2); + } else { + if ( + dblquote && + c != '\\' && c != '`' && + c != '$' && ( + c != '"' || + eofmark != NULL + ) + ) { + USTPUTC('\\', out); + } + USTPUTC(CTLESC, out); + USTPUTC(c, out); + quotef++; + } + break; + case CSQUOTE: + syntax = SQSYNTAX; +quotemark: + if (eofmark == NULL) { + USTPUTC(CTLQUOTEMARK, out); + } + break; + case CDQUOTE: + syntax = DQSYNTAX; + dblquote = 1; + goto quotemark; + case CENDQUOTE: + if (eofmark && !varnest) + USTPUTC(c, out); + else { + if (dqvarnest == 0) { + syntax = BASESYNTAX; + dblquote = 0; + } + quotef++; + goto quotemark; + } + break; + case CVAR: /* '$' */ + PARSESUB(); /* parse substitution */ + break; + case CENDVAR: /* '}' */ + if (varnest > 0) { + varnest--; + if (dqvarnest > 0) { + dqvarnest--; + } + USTPUTC(CTLENDVAR, out); + } else { + USTPUTC(c, out); + } + break; + case CLP: /* '(' in arithmetic */ + parenlevel++; + USTPUTC(c, out); + break; + case CRP: /* ')' in arithmetic */ + if (parenlevel > 0) { + USTPUTC(c, out); + --parenlevel; + } else { + if (pgetc() == ')') { + USTPUTC(CTLENDARI, out); + if (!--arinest) + syntax = prevsyntax; + } else { + /* + * unbalanced parens + * (don't 2nd guess - no error) + */ + pungetc(); + USTPUTC(')', out); + } + } + break; + case CBQUOTE: /* '`' */ + PARSEBACKQOLD(); + break; + case CEOF: + goto endword; /* exit outer loop */ + case CIGN: + break; + default: + if (varnest == 0) + goto endword; /* exit outer loop */ + if (c != PEOA) { + USTPUTC(c, out); + } + } + c = pgetc_macro(); + } + } +endword: + if (syntax == ARISYNTAX) + synerror("Missing '))'"); + if (syntax != BASESYNTAX && eofmark == NULL) + synerror("Unterminated quoted string"); + if (varnest != 0) { + /* { */ + synerror("Missing '}'"); + } + USTPUTC('\0', out); + len = out - (char *)stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + PARSEREDIR(); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + return lasttoken = TWORD; +/* end of readtoken routine */ + + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + */ + +checkend: { + if (realeofmark(eofmark)) { + int markloc; + char *p; + + if (c == PEOA) { + c = pgetc2(); + } + if (striptabs) { + while (c == '\t') { + c = pgetc2(); + } + } + + markloc = out - (char *)stackblock(); + for (p = eofmark; STPUTC(c, out), *p; p++) { + if (c != *p) + goto more_heredoc; + + c = pgetc2(); + } + + if (c == '\n' || c == PEOF) { + c = PEOF; + plinno++; + needprompt = doprompt; + } else { + int len; + +more_heredoc: + p = (char *)stackblock() + markloc + 1; + len = out - p; + + if (len) { + len -= c < 0; + c = p[-1]; + + if (len) { + char *str; + + str = alloca(len + 1); + *(char *)mempcpy(str, p, len) = 0; + + pushstring(str, NULL); + } + } + } + + STADJUST((char *)stackblock() + markloc - out, out); + } + goto checkend_return; +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +parseredir: { + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc(); + if (c == '>') + np->type = NAPPEND; + else if (c == '|') + np->type = NCLOBBER; + else if (c == '&') + np->type = NTOFD; + else { + np->type = NTO; + pungetc(); + } + } else { /* c == '<' */ + np->nfile.fd = 0; + switch (c = pgetc()) { + case '<': + if (sizeof (struct nfile) != sizeof (struct nhere)) { + np = (union node *)stalloc(sizeof (struct nhere)); + np->nfile.fd = 0; + } + np->type = NHERE; + heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); + heredoc->here = np; + if ((c = pgetc()) == '-') { + heredoc->striptabs = 1; + } else { + heredoc->striptabs = 0; + pungetc(); + } + break; + + case '&': + np->type = NFROMFD; + break; + + case '>': + np->type = NFROMTO; + break; + + default: + np->type = NFROM; + pungetc(); + break; + } + } + if (fd != '\0') + np->nfile.fd = digit_val(fd); + redirnode = np; + goto parseredir_return; +} + + +/* + * Parse a substitution. At this point, we have read the dollar sign + * and nothing else. + */ + +parsesub: { + int subtype; + int typeloc; + char *p; + static const char types[] = "}-+?="; + + c = pgetc(); + if ( + (checkkwd & CHKEOFMARK) || + c <= PEOA || + (c != '(' && c != '{' && !is_name(c) && !is_special(c)) + ) { + USTPUTC('$', out); + pungetc(); + } else if (c == '(') { /* $(command) or $((arith)) */ + if (pgetc() == '(') { + PARSEARITH(); + } else { + pungetc(); + PARSEBACKQNEW(); + } + } else { + USTPUTC(CTLVAR, out); + typeloc = out - (char *)stackblock(); + STADJUST(1, out); + subtype = VSNORMAL; + if (likely(c == '{')) { + c = pgetc(); + subtype = 0; + } +varname: + if (is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_in_name(c)); + } else if (is_digit(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_digit(c)); + } + else if (is_special(c)) { + int cc = c; + + c = pgetc(); + + if (!subtype && cc == '#') { + subtype = VSLENGTH; + + if (c == '_' || isalnum(c)) + goto varname; + + cc = c; + c = pgetc(); + if (cc == '}' || c != '}') { + pungetc(); + subtype = 0; + c = cc; + cc = '#'; + } + } + + USTPUTC(cc, out); + } + else + goto badsub; + + if (subtype == 0) { + switch (c) { + case ':': + subtype = VSNUL; + c = pgetc(); + /*FALLTHROUGH*/ + default: + p = strchr(types, c); + if (p == NULL) + break; + subtype |= p - types + VSNORMAL; + break; + case '%': + case '#': + { + int cc = c; + subtype = c == '#' ? VSTRIMLEFT : + VSTRIMRIGHT; + c = pgetc(); + if (c == cc) + subtype++; + else + pungetc(); + break; + } + } + } else { +badsub: + pungetc(); + } + *((char *)stackblock() + typeloc) = subtype; + if (subtype != VSNORMAL) { + varnest++; + if (dblquote) + dqvarnest++; + } + STPUTC('=', out); + } + goto parsesub_return; +} + + +/* + * Called to parse command substitutions. Newstyle is set if the command + * is enclosed inside $(...); nlpp is a pointer to the head of the linked + * list of commands (passed by reference), and savelen is the number of + * characters on the top of the stack which must be preserved. + */ + +parsebackq: { + struct nodelist **nlpp; + union node *n; + char *str; + size_t savelen; + int uninitialized_var(saveprompt); + + str = NULL; + savelen = out - (char *)stackblock(); + if (savelen > 0) { + str = alloca(savelen); + memcpy(str, stackblock(), savelen); + } + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + char *pout; + int pc; + size_t psavelen; + char *pstr; + + + STARTSTACKSTR(pout); + for (;;) { + if (needprompt) { + setprompt(2); + } + switch (pc = pgetc()) { + case '`': + goto done; + + case '\\': + if ((pc = pgetc()) == '\n') { + plinno++; + if (doprompt) + setprompt(2); + /* + * If eating a newline, avoid putting + * the newline into the new character + * stream (via the STPUTC after the + * switch). + */ + continue; + } + if (pc != '\\' && pc != '`' && pc != '$' + && (!dblquote || pc != '"')) + STPUTC('\\', pout); + if (pc > PEOA) { + break; + } + /* fall through */ + + case PEOF: + case PEOA: + synerror("EOF in backquote substitution"); + + case '\n': + plinno++; + needprompt = doprompt; + break; + + default: + break; + } + STPUTC(pc, pout); + } +done: + STPUTC('\0', pout); + psavelen = pout - (char *)stackblock(); + if (psavelen > 0) { + pstr = grabstackstr(pout); + setinputstring(pstr); + } + } + nlpp = &bqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + + if (oldstyle) { + saveprompt = doprompt; + doprompt = 0; + } + + n = list(2); + + if (oldstyle) + doprompt = saveprompt; + else { + if (readtoken() != TRP) + synexpect(TRP); + } + + (*nlpp)->n = n; + if (oldstyle) { + /* + * Start reading from old file again, ignoring any pushed back + * tokens left from the backquote parsing + */ + popfile(); + tokpushback = 0; + } + while (stackblocksize() <= savelen) + growstackblock(); + STARTSTACKSTR(out); + if (str) { + memcpy(out, str, savelen); + STADJUST(savelen, out); + } + USTPUTC(CTLBACKQ, out); + if (oldstyle) + goto parsebackq_oldreturn; + else + goto parsebackq_newreturn; +} + +/* + * Parse an arithmetic expansion (indicate start of one and set state) + */ +parsearith: { + + if (++arinest == 1) { + prevsyntax = syntax; + syntax = ARISYNTAX; + } + USTPUTC(CTLARI, out); + goto parsearith_return; +} + +} /* end of readtoken */ + + + +#ifdef mkinit +INCLUDE "parser.h" +RESET { + tokpushback = 0; + checkkwd = 0; +} +#endif + + +/* + * Return of a legal variable name (a letter or underscore followed by zero or + * more letters, underscores, and digits). + */ + +char * +endofname(const char *name) + { + char *p; + + p = (char *) name; + if (! is_name(*p)) + return p; + while (*++p) { + if (! is_in_name(*p)) + break; + } + return p; +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +STATIC void +synexpect(int token) +{ + char msg[64]; + + if (token >= 0) { + fmtstr(msg, 64, "%s unexpected (expecting %s)", + tokname[lasttoken], tokname[token]); + } else { + fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); + } + synerror(msg); + /* NOTREACHED */ +} + + +STATIC void +synerror(const char *msg) +{ + errlinno = plinno; + sh_error("Syntax error: %s", msg); + /* NOTREACHED */ +} + +STATIC void +setprompt(int which) +{ + struct stackmark smark; + int show; + + needprompt = 0; + whichprompt = which; + +#ifdef SMALL + show = 1; +#else + show = !el; +#endif + if (show) { + pushstackmark(&smark, stackblocksize()); + out2str(getprompt(NULL)); + popstackmark(&smark); + } +} + +const char * +expandstr(const char *ps) +{ + union node n; + int saveprompt; + + /* XXX Fix (char *) cast. */ + setinputstring((char *)ps); + + saveprompt = doprompt; + doprompt = 0; + + readtoken1(pgetc(), DQSYNTAX, FAKEEOFMARK, 0); + + doprompt = saveprompt; + + popfile(); + + n.narg.type = NARG; + n.narg.next = NULL; + n.narg.text = wordtext; + n.narg.backquote = backquotelist; + + expandarg(&n, NULL, EXP_QUOTED); + return stackblock(); +} + +/* + * called by editline -- any expansions to the prompt + * should be added here. + */ +const char * +getprompt(void *unused) +{ + const char *prompt; + + switch (whichprompt) { + default: +#ifdef DEBUG + return "<internal prompt error>"; +#endif + case 0: + return nullstr; + case 1: + prompt = ps1val(); + break; + case 2: + prompt = ps2val(); + break; + } + + return expandstr(prompt); +} + +const char *const * +findkwd(const char *s) +{ + return findstring( + s, parsekwd, sizeof(parsekwd) / sizeof(const char *) + ); +} diff --git a/usr/dash/parser.h b/usr/dash/parser.h new file mode 100644 index 0000000..e6caed6 --- /dev/null +++ b/usr/dash/parser.h @@ -0,0 +1,93 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)parser.h 8.3 (Berkeley) 5/4/95 + */ + +/* control characters in argument strings */ +#define CTL_FIRST -127 /* first 'special' character */ +#define CTLESC -127 /* escape next character */ +#define CTLVAR -126 /* variable defn */ +#define CTLENDVAR -125 +#define CTLBACKQ -124 +#define CTLARI -122 /* arithmetic expression */ +#define CTLENDARI -121 +#define CTLQUOTEMARK -120 +#define CTL_LAST -120 /* last 'special' character */ + +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 0x0f /* type of variable substitution */ +#define VSNUL 0x10 /* colon--treat the empty string as unset */ + +/* values of VSTYPE field */ +#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ +#define VSMINUS 0x2 /* ${var-text} */ +#define VSPLUS 0x3 /* ${var+text} */ +#define VSQUESTION 0x4 /* ${var?message} */ +#define VSASSIGN 0x5 /* ${var=text} */ +#define VSTRIMRIGHT 0x6 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ +#define VSTRIMLEFT 0x8 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ +#define VSLENGTH 0xa /* ${#var} */ + +/* values of checkkwd variable */ +#define CHKALIAS 0x1 +#define CHKKWD 0x2 +#define CHKNL 0x4 +#define CHKEOFMARK 0x8 + + +/* + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL, so we use the address of a variable that + * happens to be handy. + */ +extern int tokpushback; +#define NEOF ((union node *)&tokpushback) +extern int whichprompt; /* 1 == PS1, 2 == PS2 */ +extern int checkkwd; + + +union node *parsecmd(int); +void fixredir(union node *, const char *, int); +const char *getprompt(void *); +const char *const *findkwd(const char *); +char *endofname(const char *); +const char *expandstr(const char *); + +static inline int +goodname(const char *p) +{ + return !*endofname(p); +} diff --git a/usr/dash/redir.c b/usr/dash/redir.c new file mode 100644 index 0000000..f96a76b --- /dev/null +++ b/usr/dash/redir.c @@ -0,0 +1,459 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/param.h> /* PIPE_BUF */ +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +/* + * Code for dealing with input/output redirection. + */ + +#include "main.h" +#include "shell.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "expand.h" +#include "redir.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" + + +#define REALLY_CLOSED -3 /* fd that was closed and still is */ +#define EMPTY -2 /* marks an unused slot in redirtab */ +#define CLOSED -1 /* fd opened for redir needs to be closed */ + +#ifndef PIPE_BUF +# define PIPESIZE 4096 /* amount of buffering in a pipe */ +#else +# define PIPESIZE PIPE_BUF +#endif + + +MKINIT +struct redirtab { + struct redirtab *next; + int renamed[10]; +}; + + +MKINIT struct redirtab *redirlist; + +STATIC int openredirect(union node *); +#ifdef notyet +STATIC void dupredirect(union node *, int, char[10]); +#else +STATIC void dupredirect(union node *, int); +#endif +STATIC int openhere(union node *); + + +/* + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout, is saved in memory. + */ + +void +redirect(union node *redir, int flags) +{ + union node *n; + struct redirtab *sv; + int i; + int fd; + int newfd; + int *p; +#if notyet + char memory[10]; /* file descriptors to write to memory */ + + for (i = 10 ; --i >= 0 ; ) + memory[i] = 0; + memory[1] = flags & REDIR_BACKQ; +#endif + if (!redir) + return; + sv = NULL; + INTOFF; + if (likely(flags & REDIR_PUSH)) + sv = redirlist; + n = redir; + do { + newfd = openredirect(n); + if (newfd < -1) + continue; + + fd = n->nfile.fd; + + if (sv) { + p = &sv->renamed[fd]; + i = *p; + + if (likely(i == EMPTY)) { + i = CLOSED; + if (fd != newfd) { + i = savefd(fd, fd); + fd = -1; + } + } + + if (i == newfd) + /* Can only happen if i == newfd == CLOSED */ + i = REALLY_CLOSED; + + *p = i; + } + + if (fd == newfd) + continue; + +#ifdef notyet + dupredirect(n, newfd, memory); +#else + dupredirect(n, newfd); +#endif + } while ((n = n->nfile.next)); + INTON; +#ifdef notyet + if (memory[1]) + out1 = &memout; + if (memory[2]) + out2 = &memout; +#endif + if (flags & REDIR_SAVEFD2 && sv->renamed[2] >= 0) + preverrout.fd = sv->renamed[2]; +} + + +STATIC int +openredirect(union node *redir) +{ + struct stat64 sb; + char *fname; + int f; + + switch (redir->nfile.type) { + case NFROM: + fname = redir->nfile.expfname; + if ((f = open64(fname, O_RDONLY)) < 0) + goto eopen; + break; + case NFROMTO: + fname = redir->nfile.expfname; + if ((f = open64(fname, O_RDWR|O_CREAT, 0666)) < 0) + goto ecreate; + break; + case NTO: + /* Take care of noclobber mode. */ + if (Cflag) { + fname = redir->nfile.expfname; + if (stat64(fname, &sb) < 0) { + if ((f = open64(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) + goto ecreate; + } else if (!S_ISREG(sb.st_mode)) { + if ((f = open64(fname, O_WRONLY, 0666)) < 0) + goto ecreate; + if (fstat64(f, &sb) < 0 && S_ISREG(sb.st_mode)) { + close(f); + errno = EEXIST; + goto ecreate; + } + } else { + errno = EEXIST; + goto ecreate; + } + break; + } + /* FALLTHROUGH */ + case NCLOBBER: + fname = redir->nfile.expfname; + if ((f = open64(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + goto ecreate; + break; + case NAPPEND: + fname = redir->nfile.expfname; + if ((f = open64(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + goto ecreate; + break; + case NTOFD: + case NFROMFD: + f = redir->ndup.dupfd; + if (f == redir->nfile.fd) + f = -2; + break; + default: +#ifdef DEBUG + abort(); +#endif + /* Fall through to eliminate warning. */ + case NHERE: + case NXHERE: + f = openhere(redir); + break; + } + + return f; +ecreate: + sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +eopen: + sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); +} + + +STATIC void +#ifdef notyet +dupredirect(redir, f, memory) +#else +dupredirect(redir, f) +#endif + union node *redir; + int f; +#ifdef notyet + char memory[10]; +#endif + { + int fd = redir->nfile.fd; + int err = 0; + +#ifdef notyet + memory[fd] = 0; +#endif + if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { + /* if not ">&-" */ + if (f >= 0) { +#ifdef notyet + if (memory[f]) + memory[fd] = 1; + else +#endif + if (dup2(f, fd) < 0) { + err = errno; + goto err; + } + return; + } + f = fd; + } else if (dup2(f, fd) < 0) + err = errno; + + close(f); + if (err < 0) + goto err; + + return; + +err: + sh_error("%d: %s", f, strerror(err)); +} + + +/* + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. + */ + +STATIC int +openhere(union node *redir) +{ + char *p; + int pip[2]; + size_t len = 0; + + if (pipe(pip) < 0) + sh_error("Pipe call failed"); + + p = redir->nhere.doc->narg.text; + if (redir->type == NXHERE) { + expandarg(redir->nhere.doc, NULL, EXP_QUOTED); + p = stackblock(); + } + + len = strlen(p); + if (len <= PIPESIZE) { + xwrite(pip[1], p, len); + goto out; + } + + if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + signal(SIGPIPE, SIG_DFL); + xwrite(pip[1], p, len); + _exit(0); + } +out: + close(pip[1]); + return pip[0]; +} + + + +/* + * Undo the effects of the last redirection. + */ + +void +popredir(int drop) +{ + struct redirtab *rp; + int i; + + INTOFF; + rp = redirlist; + for (i = 0 ; i < 10 ; i++) { + switch (rp->renamed[i]) { + case CLOSED: + if (!drop) + close(i); + break; + case EMPTY: + case REALLY_CLOSED: + break; + default: + if (!drop) + dup2(rp->renamed[i], i); + close(rp->renamed[i]); + break; + } + } + redirlist = rp->next; + ckfree(rp); + INTON; +} + +/* + * Undo all redirections. Called on error or interrupt. + */ + +#ifdef mkinit + +INCLUDE "redir.h" + +RESET { + /* + * Discard all saved file descriptors. + */ + unwindredir(0); +} + +#endif + + + +/* + * Move a file descriptor to > 10. Invokes sh_error on error unless + * the original file dscriptor is not open. + */ + +int +savefd(int from, int ofd) +{ + int newfd; + int err; + + newfd = fcntl(from, F_DUPFD, 10); + err = newfd < 0 ? errno : 0; + if (err != EBADF) { + close(ofd); + if (err) + sh_error("%d: %s", from, strerror(err)); + else + fcntl(newfd, F_SETFD, FD_CLOEXEC); + } + + return newfd; +} + + +int +redirectsafe(union node *redir, int flags) +{ + int err; + volatile int saveint; + struct jmploc *volatile savehandler = handler; + struct jmploc jmploc; + + SAVEINT(saveint); + if (!(err = setjmp(jmploc.loc) * 2)) { + handler = &jmploc; + redirect(redir, flags); + } + handler = savehandler; + if (err && exception != EXERROR) + longjmp(handler->loc, 1); + RESTOREINT(saveint); + return err; +} + + +void unwindredir(struct redirtab *stop) +{ + while (redirlist != stop) + popredir(0); +} + + +struct redirtab *pushredir(union node *redir) +{ + struct redirtab *sv; + struct redirtab *q; + int i; + + q = redirlist; + if (!redir) + goto out; + + sv = ckmalloc(sizeof (struct redirtab)); + sv->next = q; + redirlist = sv; + for (i = 0; i < 10; i++) + sv->renamed[i] = EMPTY; + +out: + return q; +} diff --git a/usr/dash/redir.h b/usr/dash/redir.h new file mode 100644 index 0000000..8e56995 --- /dev/null +++ b/usr/dash/redir.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)redir.h 8.2 (Berkeley) 5/4/95 + */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#ifdef notyet +#define REDIR_BACKQ 02 /* save the command output in memory */ +#endif +#define REDIR_SAVEFD2 03 /* set preverrout */ + +struct redirtab; +union node; +void redirect(union node *, int); +void popredir(int); +void clearredir(void); +int savefd(int, int); +int redirectsafe(union node *, int); +void unwindredir(struct redirtab *stop); +struct redirtab *pushredir(union node *redir); + diff --git a/usr/dash/sh.1 b/usr/dash/sh.1 new file mode 100644 index 0000000..ff5881a --- /dev/null +++ b/usr/dash/sh.1 @@ -0,0 +1,2332 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 1997-2005 +.\" Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" +.\" 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. +.\" +.\" @(#)sh.1 8.6 (Berkeley) 5/4/95 +.\" +.Dd January 19, 2003 +.Os +.Dt SH 1 +.Sh NAME +.Nm sh +.Nd command interpreter (shell) +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Op Ar command_file Oo Ar argument ... Oc +.Ek +.Nm +.Fl c +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Ar command_string +.Op Ar command_name Oo Ar argument ... Oc +.Ek +.Nm +.Fl s +.Bk -words +.Op Fl aCefnuvxIimqVEb +.Op Cm +aCefnuvxIimqVEb +.Ek +.Bk -words +.Op Fl o Ar option_name +.Op Cm +o Ar option_name +.Ek +.Bk -words +.Op Ar argument ... +.Ek +.Sh DESCRIPTION +.Nm +is the standard command interpreter for the system. +The current version of +.Nm +is in the process of being changed to conform with the +.Tn POSIX +1003.2 and 1003.2a specifications for the shell. +This version has many +features which make it appear similar in some respects to the Korn shell, +but it is not a Korn shell clone (see +.Xr ksh 1 ) . +Only features designated by +.Tn POSIX , +plus a few Berkeley extensions, are being incorporated into this shell. +We expect +.Tn POSIX +conformance by the time 4.4 BSD is released. +This man page is not intended +to be a tutorial or a complete specification of the shell. +.Ss Overview +The shell is a command that reads lines from either a file or the +terminal, interprets them, and generally executes other commands. +It is the program that is running when a user logs into the system +(although a user can select a different shell with the +.Xr chsh 1 +command). +The shell implements a language that has flow control +constructs, a macro facility that provides a variety of features in +addition to data storage, along with built in history and line editing +capabilities. +It incorporates many features to aid interactive use and +has the advantage that the interpretative language is common to both +interactive and non-interactive use (shell scripts). +That is, commands +can be typed directly to the running shell or can be put into a file and +the file can be executed directly by the shell. +.Ss Invocation +If no args are present and if the standard input of the shell +is connected to a terminal (or if the +.Fl i +flag is set), +and the +.Fl c +option is not present, the shell is considered an interactive shell. +An interactive shell generally prompts before each command and handles +programming and command errors differently (as described below). +When first starting, +the shell inspects argument 0, and if it begins with a dash +.Sq - , +the shell is also considered +a login shell. +This is normally done automatically by the system +when the user first logs in. +A login shell first reads commands +from the files +.Pa /etc/profile +and +.Pa .profile +if they exist. +If the environment variable +.Ev ENV +is set on entry to an interactive shell, or is set in the +.Pa .profile +of a login shell, the shell next reads +commands from the file named in +.Ev ENV . +Therefore, a user should place commands that are to be executed only at +login time in the +.Pa .profile +file, and commands that are executed for every interactive shell inside the +.Ev ENV +file. +To set the +.Ev ENV +variable to some file, place the following line in your +.Pa .profile +of your home directory +.Pp +.Dl ENV=$HOME/.shinit; export ENV +.Pp +substituting for +.Dq .shinit +any filename you wish. +.Pp +If command line arguments besides the options have been specified, then +the shell treats the first argument as the name of a file from which to +read commands (a shell script), and the remaining arguments are set as the +positional parameters of the shell ($1, $2, etc). +Otherwise, the shell +reads commands from its standard input. +.Ss Argument List Processing +All of the single letter options have a corresponding name that can be +used as an argument to the +.Fl o +option. +The set +.Fl o +name is provided next to the single letter option in +the description below. +Specifying a dash +.Dq - +turns the option on, while using a plus +.Dq + +disables the option. +The following options can be set from the command line or +with the +.Ic set +builtin (described later). +.Bl -tag -width aaaallexportfoo -offset indent +.It Fl a Em allexport +Export all variables assigned to. +.It Fl c +Read commands from the +.Ar command_string +operand instead of from the standard input. +Special parameter 0 will be set from the +.Ar command_name +operand and the positional parameters ($1, $2, etc.) +set from the remaining argument operands. +.It Fl C Em noclobber +Don't overwrite existing files with +.Dq \*[Gt] . +.It Fl e Em errexit +If not interactive, exit immediately if any untested command fails. +The exit status of a command is considered to be +explicitly tested if the command is used to control an +.Ic if , +.Ic elif , +.Ic while , +or +.Ic until ; +or if the command is the left hand operand of an +.Dq && +or +.Dq || +operator. +.It Fl f Em noglob +Disable pathname expansion. +.It Fl n Em noexec +If not interactive, read commands but do not execute them. +This is useful for checking the syntax of shell scripts. +.It Fl u Em nounset +Write a message to standard error when attempting to expand a variable +that is not set, and if the shell is not interactive, exit immediately. +.It Fl v Em verbose +The shell writes its input to standard error as it is read. +Useful for debugging. +.It Fl x Em xtrace +Write each command to standard error (preceded by a +.Sq +\ ) +before it is executed. +Useful for debugging. +.It Fl I Em ignoreeof +Ignore EOF's from input when interactive. +.It Fl i Em interactive +Force the shell to behave interactively. +.It Fl m Em monitor +Turn on job control (set automatically when interactive). +.It Fl s Em stdin +Read commands from standard input (set automatically if no file arguments +are present). +This option has no effect when set after the shell has +already started running (i.e. with +.Ic set ) . +.It Fl V Em vi +Enable the built-in +.Xr vi 1 +command line editor (disables +.Fl E +if it has been set). +.It Fl E Em emacs +Enable the built-in +.Xr emacs 1 +command line editor (disables +.Fl V +if it has been set). +.It Fl b Em notify +Enable asynchronous notification of background job completion. +(UNIMPLEMENTED for 4.4alpha) +.El +.Ss Lexical Structure +The shell reads input in terms of lines from a file and breaks it up into +words at whitespace (blanks and tabs), and at certain sequences of +characters that are special to the shell called +.Dq operators . +There are two types of operators: control operators and redirection +operators (their meaning is discussed later). +Following is a list of operators: +.Bl -ohang -offset indent +.It "Control operators:" +.Dl & && \&( \&) \&; ;; | || \*[Lt]newline\*[Gt] +.It "Redirection operators:" +.Dl \*[Lt] \*[Gt] \*[Gt]| \*[Lt]\*[Lt] \*[Gt]\*[Gt] \*[Lt]& \*[Gt]& \*[Lt]\*[Lt]- \*[Lt]\*[Gt] +.El +.Ss Quoting +Quoting is used to remove the special meaning of certain characters or +words to the shell, such as operators, whitespace, or keywords. +There are three types of quoting: matched single quotes, +matched double quotes, and backslash. +.Ss Backslash +A backslash preserves the literal meaning of the following +character, with the exception of +.Aq newline . +A backslash preceding a +.Aq newline +is treated as a line continuation. +.Ss Single Quotes +Enclosing characters in single quotes preserves the literal meaning of all +the characters (except single quotes, making it impossible to put +single-quotes in a single-quoted string). +.Ss Double Quotes +Enclosing characters within double quotes preserves the literal +meaning of all characters except dollarsign +.Pq $ , +backquote +.Pq ` , +and backslash +.Pq \e . +The backslash inside double quotes is historically weird, and serves to +quote only the following characters: +.Dl $ ` \*q \e \*[Lt]newline\*[Gt] . +Otherwise it remains literal. +.Ss Reserved Words +Reserved words are words that have special meaning to the +shell and are recognized at the beginning of a line and +after a control operator. +The following are reserved words: +.Bl -column while while while while while -offset indent +.It ! Ta elif Ta fi Ta while Ta case +.It else Ta for Ta then Ta { Ta } +.It do Ta done Ta until Ta if Ta esac +.El +.Pp +Their meaning is discussed later. +.Ss Aliases +An alias is a name and corresponding value set using the +.Xr alias 1 +builtin command. +Whenever a reserved word may occur (see above), +and after checking for reserved words, the shell +checks the word to see if it matches an alias. +If it does, it replaces it in the input stream with its value. +For example, if there is an alias called +.Dq lf +with the value +.Dq "ls -F" , +then the input: +.Pp +.Dl lf foobar Aq return +.Pp +would become +.Pp +.Dl ls -F foobar Aq return +.Pp +Aliases provide a convenient way for naive users to create shorthands for +commands without having to learn how to create functions with arguments. +They can also be used to create lexically obscure code. +This use is discouraged. +.Ss Commands +The shell interprets the words it reads according to a language, the +specification of which is outside the scope of this man page (refer to the +BNF in the +.Tn POSIX +1003.2 document). +Essentially though, a line is read and if the first +word of the line (or after a control operator) is not a reserved word, +then the shell has recognized a simple command. +Otherwise, a complex +command or some other special construct may have been recognized. +.Ss Simple Commands +If a simple command has been recognized, the shell performs +the following actions: +.Bl -enum -offset indent +.It +Leading words of the form +.Dq name=value +are stripped off and assigned to the environment of the simple command. +Redirection operators and their arguments (as described below) are +stripped off and saved for processing. +.It +The remaining words are expanded as described in +the section called +.Dq Expansions , +and the first remaining word is considered the command name and the +command is located. +The remaining words are considered the arguments of the command. +If no command name resulted, then the +.Dq name=value +variable assignments recognized in item 1 affect the current shell. +.It +Redirections are performed as described in the next section. +.El +.Ss Redirections +Redirections are used to change where a command reads its input or sends +its output. +In general, redirections open, close, or duplicate an +existing reference to a file. +The overall format used for redirection is: +.Pp +.Dl [n] Va redir-op Ar file +.Pp +where +.Va redir-op +is one of the redirection operators mentioned previously. +Following is a list of the possible redirections. +The +.Bq n +is an optional number, as in +.Sq 3 +(not +.Sq Bq 3 , +that refers to a file descriptor. +.Bl -tag -width aaabsfiles -offset indent +.It [n] Ns \*[Gt] file +Redirect standard output (or n) to file. +.It [n] Ns \*[Gt]| file +Same, but override the +.Fl C +option. +.It [n] Ns \*[Gt]\*[Gt] file +Append standard output (or n) to file. +.It [n] Ns \*[Lt] file +Redirect standard input (or n) from file. +.It [n1] Ns \*[Lt]& Ns n2 +Duplicate standard input (or n1) from file descriptor n2. +.It [n] Ns \*[Lt]&- +Close standard input (or n). +.It [n1] Ns \*[Gt]& Ns n2 +Duplicate standard output (or n1) to n2. +.It [n] Ns \*[Gt]&- +Close standard output (or n). +.It [n] Ns \*[Lt]\*[Gt] file +Open file for reading and writing on standard input (or n). +.El +.Pp +The following redirection is often called a +.Dq here-document . +.Bl -item -offset indent +.It +.Li [n]\*[Lt]\*[Lt] delimiter +.Dl here-doc-text ... +.Li delimiter +.El +.Pp +All the text on successive lines up to the delimiter is saved away and +made available to the command on standard input, or file descriptor n if +it is specified. +If the delimiter as specified on the initial line is +quoted, then the here-doc-text is treated literally, otherwise the text is +subjected to parameter expansion, command substitution, and arithmetic +expansion (as described in the section on +.Dq Expansions ) . +If the operator is +.Dq \*[Lt]\*[Lt]- +instead of +.Dq \*[Lt]\*[Lt] , +then leading tabs in the here-doc-text are stripped. +.Ss Search and Execution +There are three types of commands: shell functions, builtin commands, and +normal programs -- and the command is searched for (by name) in that order. +They each are executed in a different way. +.Pp +When a shell function is executed, all of the shell positional parameters +(except $0, which remains unchanged) are set to the arguments of the shell +function. +The variables which are explicitly placed in the environment of +the command (by placing assignments to them before the function name) are +made local to the function and are set to the values given. +Then the command given in the function definition is executed. +The positional parameters are restored to their original values +when the command completes. +This all occurs within the current shell. +.Pp +Shell builtins are executed internally to the shell, without spawning a +new process. +.Pp +Otherwise, if the command name doesn't match a function or builtin, the +command is searched for as a normal program in the file system (as +described in the next section). +When a normal program is executed, the shell runs the program, +passing the arguments and the environment to the program. +If the program is not a normal executable file (i.e., if it does +not begin with the "magic number" whose +.Tn ASCII +representation is "#!", so +.Xr execve 2 +returns +.Er ENOEXEC +then) the shell will interpret the program in a subshell. +The child shell will reinitialize itself in this case, +so that the effect will be as if a +new shell had been invoked to handle the ad-hoc shell script, except that +the location of hashed commands located in the parent shell will be +remembered by the child. +.Pp +Note that previous versions of this document and the source code itself +misleadingly and sporadically refer to a shell script without a magic +number as a "shell procedure". +.Ss Path Search +When locating a command, the shell first looks to see if it has a shell +function by that name. +Then it looks for a builtin command by that name. +If a builtin command is not found, one of two things happen: +.Bl -enum +.It +Command names containing a slash are simply executed without performing +any searches. +.It +The shell searches each entry in +.Ev PATH +in turn for the command. +The value of the +.Ev PATH +variable should be a series of entries separated by colons. +Each entry consists of a directory name. +The current directory may be indicated +implicitly by an empty directory name, or explicitly by a single period. +.El +.Ss Command Exit Status +Each command has an exit status that can influence the behaviour +of other shell commands. +The paradigm is that a command exits +with zero for normal or success, and non-zero for failure, +error, or a false indication. +The man page for each command +should indicate the various exit codes and what they mean. +Additionally, the builtin commands return exit codes, as does +an executed shell function. +.Pp +If a command consists entirely of variable assignments then the +exit status of the command is that of the last command substitution +if any, otherwise 0. +.Ss Complex Commands +Complex commands are combinations of simple commands with control +operators or reserved words, together creating a larger complex command. +More generally, a command is one of the following: +.Bl -bullet +.It +simple command +.It +pipeline +.It +list or compound-list +.It +compound command +.It +function definition +.El +.Pp +Unless otherwise stated, the exit status of a command is that of the last +simple command executed by the command. +.Ss Pipelines +A pipeline is a sequence of one or more commands separated +by the control operator |. +The standard output of all but +the last command is connected to the standard input +of the next command. +The standard output of the last +command is inherited from the shell, as usual. +.Pp +The format for a pipeline is: +.Pp +.Dl [!] command1 [ | command2 ...] +.Pp +The standard output of command1 is connected to the standard input of +command2. +The standard input, standard output, or both of a command is +considered to be assigned by the pipeline before any redirection specified +by redirection operators that are part of the command. +.Pp +If the pipeline is not in the background (discussed later), the shell +waits for all commands to complete. +.Pp +If the reserved word ! does not precede the pipeline, the exit status is +the exit status of the last command specified in the pipeline. +Otherwise, the exit status is the logical NOT of the exit status of the +last command. +That is, if the last command returns zero, the exit status +is 1; if the last command returns greater than zero, the exit status is +zero. +.Pp +Because pipeline assignment of standard input or standard output or both +takes place before redirection, it can be modified by redirection. +For example: +.Pp +.Dl $ command1 2\*[Gt]&1 | command2 +.Pp +sends both the standard output and standard error of command1 +to the standard input of command2. +.Pp +A ; or +.Aq newline +terminator causes the preceding AND-OR-list (described +next) to be executed sequentially; a & causes asynchronous execution of +the preceding AND-OR-list. +.Pp +Note that unlike some other shells, each process in the pipeline is a +child of the invoking shell (unless it is a shell builtin, in which case +it executes in the current shell -- but any effect it has on the +environment is wiped). +.Ss Background Commands -- & +If a command is terminated by the control operator ampersand (&), the +shell executes the command asynchronously -- that is, the shell does not +wait for the command to finish before executing the next command. +.Pp +The format for running a command in background is: +.Pp +.Dl command1 & [command2 & ...] +.Pp +If the shell is not interactive, the standard input of an asynchronous +command is set to +.Pa /dev/null . +.Ss Lists -- Generally Speaking +A list is a sequence of zero or more commands separated by newlines, +semicolons, or ampersands, and optionally terminated by one of these three +characters. +The commands in a list are executed in the order they are written. +If command is followed by an ampersand, the shell starts the +command and immediately proceed onto the next command; otherwise it waits +for the command to terminate before proceeding to the next one. +.Ss Short-Circuit List Operators +.Dq && +and +.Dq || +are AND-OR list operators. +.Dq && +executes the first command, and then executes the second command iff the +exit status of the first command is zero. +.Dq || +is similar, but executes the second command iff the exit status of the first +command is nonzero. +.Dq && +and +.Dq || +both have the same priority. +.Ss Flow-Control Constructs -- if, while, for, case +The syntax of the if command is +.Bd -literal -offset indent +if list +then list +[ elif list +then list ] ... +[ else list ] +fi +.Ed +.Pp +The syntax of the while command is +.Bd -literal -offset indent +while list +do list +done +.Ed +.Pp +The two lists are executed repeatedly while the exit status of the +first list is zero. +The until command is similar, but has the word +until in place of while, which causes it to +repeat until the exit status of the first list is zero. +.Pp +The syntax of the for command is +.Bd -literal -offset indent +for variable in word ... +do list +done +.Ed +.Pp +The words are expanded, and then the list is executed repeatedly with the +variable set to each word in turn. +do and done may be replaced with +.Dq { +and +.Dq } . +.Pp +The syntax of the break and continue command is +.Bd -literal -offset indent +break [ num ] +continue [ num ] +.Ed +.Pp +Break terminates the num innermost for or while loops. +Continue continues with the next iteration of the innermost loop. +These are implemented as builtin commands. +.Pp +The syntax of the case command is +.Bd -literal -offset indent +case word in +pattern) list ;; +\&... +esac +.Ed +.Pp +The pattern can actually be one or more patterns (see +.Sx Shell Patterns +described later), separated by +.Dq \*(Ba +characters. +.Ss Grouping Commands Together +Commands may be grouped by writing either +.Pp +.Dl (list) +.Pp +or +.Pp +.Dl { list; } +.Pp +The first of these executes the commands in a subshell. +Builtin commands grouped into a (list) will not affect the current shell. +The second form does not fork another shell so is slightly more efficient. +Grouping commands together this way allows you to redirect +their output as though they were one program: +.Pp +.Bd -literal -offset indent +{ printf \*q hello \*q ; printf \*q world\\n" ; } \*[Gt] greeting +.Ed +.Pp +Note that +.Dq } +must follow a control operator (here, +.Dq \&; ) +so that it is recognized as a reserved word and not as another command argument. +.Ss Functions +The syntax of a function definition is +.Pp +.Dl name ( ) command +.Pp +A function definition is an executable statement; when executed it +installs a function named name and returns an exit status of zero. +The command is normally a list enclosed between +.Dq { +and +.Dq } . +.Pp +Variables may be declared to be local to a function by using a local +command. +This should appear as the first statement of a function, and the syntax is +.Pp +.Dl local [ variable | - ] ... +.Pp +Local is implemented as a builtin command. +.Pp +When a variable is made local, it inherits the initial value and exported +and readonly flags from the variable with the same name in the surrounding +scope, if there is one. +Otherwise, the variable is initially unset. +The shell uses dynamic scoping, so that if you make the variable x local to +function f, which then calls function g, references to the variable x made +inside g will refer to the variable x declared inside f, not to the global +variable named x. +.Pp +The only special parameter that can be made local is +.Dq - . +Making +.Dq - +local any shell options that are changed via the set command inside the +function to be restored to their original values when the function +returns. +.Pp +The syntax of the return command is +.Pp +.Dl return [ exitstatus ] +.Pp +It terminates the currently executing function. +Return is implemented as a builtin command. +.Ss Variables and Parameters +The shell maintains a set of parameters. +A parameter denoted by a name is called a variable. +When starting up, the shell turns all the environment +variables into shell variables. +New variables can be set using the form +.Pp +.Dl name=value +.Pp +Variables set by the user must have a name consisting solely of +alphabetics, numerics, and underscores - the first of which must not be +numeric. +A parameter can also be denoted by a number or a special +character as explained below. +.Ss Positional Parameters +A positional parameter is a parameter denoted by a number (n \*[Gt] 0). +The shell sets these initially to the values of its command line arguments +that follow the name of the shell script. +The +.Ic set +builtin can also be used to set or reset them. +.Ss Special Parameters +A special parameter is a parameter denoted by one of the following special +characters. +The value of the parameter is listed next to its character. +.Bl -tag -width thinhyphena +.It * +Expands to the positional parameters, starting from one. +When the +expansion occurs within a double-quoted string it expands to a single +field with the value of each parameter separated by the first character of +the +.Ev IFS +variable, or by a +.Aq space +if +.Ev IFS +is unset. +.It @ +Expands to the positional parameters, starting from one. +When the expansion occurs within double-quotes, each positional +parameter expands as a separate argument. +If there are no positional parameters, the +expansion of @ generates zero arguments, even when @ is +double-quoted. +What this basically means, for example, is +if $1 is +.Dq abc +and $2 is +.Dq def ghi , +then +.Qq $@ +expands to +the two arguments: +.Pp +.Sm off +.Dl \*q abc \*q \ \*q def\ ghi \*q +.Sm on +.It # +Expands to the number of positional parameters. +.It ? +Expands to the exit status of the most recent pipeline. +.It - (Hyphen.) +Expands to the current option flags (the single-letter +option names concatenated into a string) as specified on +invocation, by the set builtin command, or implicitly +by the shell. +.It $ +Expands to the process ID of the invoked shell. +A subshell retains the same value of $ as its parent. +.It ! +Expands to the process ID of the most recent background +command executed from the current shell. +For a pipeline, the process ID is that of the last command in the pipeline. +.It 0 (Zero.) +Expands to the name of the shell or shell script. +.El +.Ss Word Expansions +This clause describes the various expansions that are performed on words. +Not all expansions are performed on every word, as explained later. +.Pp +Tilde expansions, parameter expansions, command substitutions, arithmetic +expansions, and quote removals that occur within a single word expand to a +single field. +It is only field splitting or pathname expansion that can +create multiple fields from a single word. +The single exception to this +rule is the expansion of the special parameter @ within double-quotes, as +was described above. +.Pp +The order of word expansion is: +.Bl -enum +.It +Tilde Expansion, Parameter Expansion, Command Substitution, +Arithmetic Expansion (these all occur at the same time). +.It +Field Splitting is performed on fields +generated by step (1) unless the +.Ev IFS +variable is null. +.It +Pathname Expansion (unless set +.Fl f +is in effect). +.It +Quote Removal. +.El +.Pp +The $ character is used to introduce parameter expansion, command +substitution, or arithmetic evaluation. +.Ss Tilde Expansion (substituting a user's home directory) +A word beginning with an unquoted tilde character (~) is +subjected to tilde expansion. +All the characters up to +a slash (/) or the end of the word are treated as a username +and are replaced with the user's home directory. +If the username is missing (as in +.Pa ~/foobar ) , +the tilde is replaced with the value of the +.Va HOME +variable (the current user's home directory). +.Ss Parameter Expansion +The format for parameter expansion is as follows: +.Pp +.Dl ${expression} +.Pp +where expression consists of all characters until the matching +.Dq } . +Any +.Dq } +escaped by a backslash or within a quoted string, and characters in +embedded arithmetic expansions, command substitutions, and variable +expansions, are not examined in determining the matching +.Dq } . +.Pp +The simplest form for parameter expansion is: +.Pp +.Dl ${parameter} +.Pp +The value, if any, of parameter is substituted. +.Pp +The parameter name or symbol can be enclosed in braces, which are +optional except for positional parameters with more than one digit or +when parameter is followed by a character that could be interpreted as +part of the name. +If a parameter expansion occurs inside double-quotes: +.Bl -enum +.It +Pathname expansion is not performed on the results of the expansion. +.It +Field splitting is not performed on the results of the +expansion, with the exception of @. +.El +.Pp +In addition, a parameter expansion can be modified by using one of the +following formats. +.Bl -tag -width aaparameterwordaaaaa +.It ${parameter:-word} +Use Default Values. +If parameter is unset or null, the expansion of word +is substituted; otherwise, the value of parameter is substituted. +.It ${parameter:=word} +Assign Default Values. +If parameter is unset or null, the expansion of +word is assigned to parameter. +In all cases, the final value of parameter is substituted. +Only variables, not positional parameters or special +parameters, can be assigned in this way. +.It ${parameter:?[word]} +Indicate Error if Null or Unset. +If parameter is unset or null, the +expansion of word (or a message indicating it is unset if word is omitted) +is written to standard error and the shell exits with a nonzero exit status. +Otherwise, the value of parameter is substituted. +An interactive shell need not exit. +.It ${parameter:+word} +Use Alternative Value. +If parameter is unset or null, null is +substituted; otherwise, the expansion of word is substituted. +.El +.Pp +In the parameter expansions shown previously, use of the colon in the +format results in a test for a parameter that is unset or null; omission +of the colon results in a test for a parameter that is only unset. +.Bl -tag -width aaparameterwordaaaaa +.It ${#parameter} +String Length. +The length in characters of the value of parameter. +.El +.Pp +The following four varieties of parameter expansion provide for substring +processing. +In each case, pattern matching notation (see +.Sx Shell Patterns ) , +rather than regular expression notation, is used to evaluate the patterns. +If parameter is * or @, the result of the expansion is unspecified. +Enclosing the full parameter expansion string in double-quotes does not +cause the following four varieties of pattern characters to be quoted, +whereas quoting characters within the braces has this effect. +.Bl -tag -width aaparameterwordaaaaa +.It ${parameter%word} +Remove Smallest Suffix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the +smallest portion of the suffix matched by the pattern deleted. +.It ${parameter%%word} +Remove Largest Suffix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the largest +portion of the suffix matched by the pattern deleted. +.It ${parameter#word} +Remove Smallest Prefix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the +smallest portion of the prefix matched by the pattern deleted. +.It ${parameter##word} +Remove Largest Prefix Pattern. +The word is expanded to produce a pattern. +The parameter expansion then results in parameter, with the largest +portion of the prefix matched by the pattern deleted. +.El +.Ss Command Substitution +Command substitution allows the output of a command to be substituted in +place of the command name itself. +Command substitution occurs when the command is enclosed as follows: +.Pp +.Dl $(command) +.Pp +or +.Po +.Dq backquoted +version +.Pc : +.Pp +.Dl `command` +.Pp +The shell expands the command substitution by executing command in a +subshell environment and replacing the command substitution with the +standard output of the command, removing sequences of one or more +.Ao newline Ac Ns s +at the end of the substitution. +(Embedded +.Ao newline Ac Ns s +before +the end of the output are not removed; however, during field splitting, +they may be translated into +.Ao space Ac Ns s , +depending on the value of +.Ev IFS +and quoting that is in effect.) +.Ss Arithmetic Expansion +Arithmetic expansion provides a mechanism for evaluating an arithmetic +expression and substituting its value. +The format for arithmetic expansion is as follows: +.Pp +.Dl $((expression)) +.Pp +The expression is treated as if it were in double-quotes, except +that a double-quote inside the expression is not treated specially. +The shell expands all tokens in the expression for parameter expansion, +command substitution, and quote removal. +.Pp +Next, the shell treats this as an arithmetic expression and +substitutes the value of the expression. +.Ss White Space Splitting (Field Splitting) +After parameter expansion, command substitution, and +arithmetic expansion the shell scans the results of +expansions and substitutions that did not occur in double-quotes for +field splitting and multiple fields can result. +.Pp +The shell treats each character of the +.Ev IFS +as a delimiter and uses the delimiters to split the results of parameter +expansion and command substitution into fields. +.Ss Pathname Expansion (File Name Generation) +Unless the +.Fl f +flag is set, file name generation is performed after word splitting is +complete. +Each word is viewed as a series of patterns, separated by slashes. +The process of expansion replaces the word with the names of all +existing files whose names can be formed by replacing each pattern with a +string that matches the specified pattern. +There are two restrictions on +this: first, a pattern cannot match a string containing a slash, and +second, a pattern cannot match a string starting with a period unless the +first character of the pattern is a period. +The next section describes the +patterns used for both Pathname Expansion and the +.Ic case +command. +.Ss Shell Patterns +A pattern consists of normal characters, which match themselves, +and meta-characters. +The meta-characters are +.Dq \&! , +.Dq * , +.Dq \&? , +and +.Dq \&[ . +These characters lose their special meanings if they are quoted. +When command or variable substitution is performed +and the dollar sign or back quotes are not double quoted, +the value of the variable or the output of +the command is scanned for these characters and they are turned into +meta-characters. +.Pp +An asterisk +.Pq Dq * +matches any string of characters. +A question mark matches any single character. +A left bracket +.Pq Dq \&[ +introduces a character class. +The end of the character class is indicated by a +.Pq Dq \&] ; +if the +.Dq \&] +is missing then the +.Dq \&[ +matches a +.Dq \&[ +rather than introducing a character class. +A character class matches any of the characters between the square brackets. +A range of characters may be specified using a minus sign. +The character class may be complemented +by making an exclamation point the first character of the character class. +.Pp +To include a +.Dq \&] +in a character class, make it the first character listed (after the +.Dq \&! , +if any). +To include a minus sign, make it the first or last character listed. +.Ss Builtins +This section lists the builtin commands which are builtin because they +need to perform some operation that can't be performed by a separate +process. +In addition to these, there are several other commands that may +be builtin for efficiency (e.g. +.Xr printf 1 , +.Xr echo 1 , +.Xr test 1 , +etc). +.Bl -tag -width 5n +.It : +.It true +A null command that returns a 0 (true) exit value. +.It \&. file +The commands in the specified file are read and executed by the shell. +.It alias Op Ar name Ns Op Ar "=string ..." +If +.Ar name=string +is specified, the shell defines the alias +.Ar name +with value +.Ar string . +If just +.Ar name +is specified, the value of the alias +.Ar name +is printed. +With no arguments, the +.Ic alias +builtin prints the +names and values of all defined aliases (see +.Ic unalias ) . +.It bg [ Ar job ] ... +Continue the specified jobs (or the current job if no +jobs are given) in the background. +.It Xo command +.Op Fl p +.Op Fl v +.Op Fl V +.Ar command +.Op Ar arg ... +.Xc +Execute the specified command but ignore shell functions when searching +for it. +(This is useful when you +have a shell function with the same name as a builtin command.) +.Bl -tag -width 5n +.It Fl p +search for command using a +.Ev PATH +that guarantees to find all the standard utilities. +.It Fl V +Do not execute the command but +search for the command and print the resolution of the +command search. +This is the same as the type builtin. +.It Fl v +Do not execute the command but +search for the command and print the absolute pathname +of utilities, the name for builtins or the expansion of aliases. +.El +.It cd Ar - +.It Xo cd Op Fl LP +.Op Ar directory +.Xc +Switch to the specified directory (default +.Ev HOME ) . +If an entry for +.Ev CDPATH +appears in the environment of the +.Ic cd +command or the shell variable +.Ev CDPATH +is set and the directory name does not begin with a slash, then the +directories listed in +.Ev CDPATH +will be searched for the specified directory. +The format of +.Ev CDPATH +is the same as that of +.Ev PATH . +If a single dash is specified as the argument, it will be replaced by the +value of +.Ev OLDPWD . +The +.Ic cd +command will print out the name of the +directory that it actually switched to if this is different from the name +that the user gave. +These may be different either because the +.Ev CDPATH +mechanism was used or because the argument is a single dash. +The +.Fl P +option causes the physical directory structure to be used, that is, all +symbolic links are resolved to their respective values. The +.Fl L +option turns off the effect of any preceding +.Fl P +options. +.It Xo echo Op Fl n +.Ar args... +.Xc +Print the arguments on the standard output, separated by spaces. +Unless the +.Fl n +option is present, a newline is output following the arguments. +.Pp +If any of the following sequences of characters is encountered during +output, the sequence is not output. Instead, the specified action is +performed: +.Bl -tag -width indent +.It Li \eb +A backspace character is output. +.It Li \ec +Subsequent output is suppressed. This is normally used at the end of the +last argument to suppress the trailing newline that +.Ic echo +would otherwise output. +.It Li \ef +Output a form feed. +.It Li \en +Output a newline character. +.It Li \er +Output a carriage return. +.It Li \et +Output a (horizontal) tab character. +.It Li \ev +Output a vertical tab. +.It Li \e0 Ns Ar digits +Output the character whose value is given by zero to three octal digits. +If there are zero digits, a nul character is output. +.It Li \e\e +Output a backslash. +.El +.Pp +All other backslash sequences elicit undefined behaviour. +.It eval Ar string ... +Concatenate all the arguments with spaces. +Then re-parse and execute the command. +.It exec Op Ar command arg ... +Unless command is omitted, the shell process is replaced with the +specified program (which must be a real program, not a shell builtin or +function). +Any redirections on the +.Ic exec +command are marked as permanent, so that they are not undone when the +.Ic exec +command finishes. +.It exit Op Ar exitstatus +Terminate the shell process. +If +.Ar exitstatus +is given it is used as the exit status of the shell; otherwise the +exit status of the preceding command is used. +.It export Ar name ... +.It export Fl p +The specified names are exported so that they will appear in the +environment of subsequent commands. +The only way to un-export a variable is to unset it. +The shell allows the value of a variable to be set at the +same time it is exported by writing +.Pp +.Dl export name=value +.Pp +With no arguments the export command lists the names of all exported variables. +With the +.Fl p +option specified the output will be formatted suitably for non-interactive use. +.It Xo fc Op Fl e Ar editor +.Op Ar first Op Ar last +.Xc +.It Xo fc Fl l +.Op Fl nr +.Op Ar first Op Ar last +.Xc +.It Xo fc Fl s Op Ar old=new +.Op Ar first +.Xc +The +.Ic fc +builtin lists, or edits and re-executes, commands previously entered +to an interactive shell. +.Bl -tag -width 5n +.It Fl e No editor +Use the editor named by editor to edit the commands. +The editor string is a command name, subject to search via the +.Ev PATH +variable. +The value in the +.Ev FCEDIT +variable is used as a default when +.Fl e +is not specified. +If +.Ev FCEDIT +is null or unset, the value of the +.Ev EDITOR +variable is used. +If +.Ev EDITOR +is null or unset, +.Xr ed 1 +is used as the editor. +.It Fl l No (ell) +List the commands rather than invoking an editor on them. +The commands are written in the sequence indicated by +the first and last operands, as affected by +.Fl r , +with each command preceded by the command number. +.It Fl n +Suppress command numbers when listing with -l. +.It Fl r +Reverse the order of the commands listed (with +.Fl l ) +or edited (with neither +.Fl l +nor +.Fl s ) . +.It Fl s +Re-execute the command without invoking an editor. +.It first +.It last +Select the commands to list or edit. +The number of previous commands that +can be accessed are determined by the value of the +.Ev HISTSIZE +variable. +The value of first or last or both are one of the following: +.Bl -tag -width 5n +.It [+]number +A positive number representing a command number; command numbers can be +displayed with the +.Fl l +option. +.It Fl number +A negative decimal number representing the command that was executed +number of commands previously. +For example, \-1 is the immediately previous command. +.El +.It string +A string indicating the most recently entered command that begins with +that string. +If the old=new operand is not also specified with +.Fl s , +the string form of the first operand cannot contain an embedded equal sign. +.El +.Pp +The following environment variables affect the execution of fc: +.Bl -tag -width HISTSIZE +.It Ev FCEDIT +Name of the editor to use. +.It Ev HISTSIZE +The number of previous commands that are accessible. +.El +.It fg Op Ar job +Move the specified job or the current job to the foreground. +.It getopts Ar optstring var +The +.Tn POSIX +.Ic getopts +command, not to be confused with the +.Em Bell Labs +-derived +.Xr getopt 1 . +.Pp +The first argument should be a series of letters, each of which may be +optionally followed by a colon to indicate that the option requires an +argument. +The variable specified is set to the parsed option. +.Pp +The +.Ic getopts +command deprecates the older +.Xr getopt 1 +utility due to its handling of arguments containing whitespace. +.Pp +The +.Ic getopts +builtin may be used to obtain options and their arguments +from a list of parameters. +When invoked, +.Ic getopts +places the value of the next option from the option string in the list in +the shell variable specified by +.Va var +and its index in the shell variable +.Ev OPTIND . +When the shell is invoked, +.Ev OPTIND +is initialized to 1. +For each option that requires an argument, the +.Ic getopts +builtin will place it in the shell variable +.Ev OPTARG . +If an option is not allowed for in the +.Va optstring , +then +.Ev OPTARG +will be unset. +.Pp +.Va optstring +is a string of recognized option letters (see +.Xr getopt 3 ) . +If a letter is followed by a colon, the option is expected to have an +argument which may or may not be separated from it by white space. +If an option character is not found where expected, +.Ic getopts +will set the variable +.Va var +to a +.Dq \&? ; +.Ic getopts +will then unset +.Ev OPTARG +and write output to standard error. +By specifying a colon as the first character of +.Va optstring +all errors will be ignored. +.Pp +A nonzero value is returned when the last option is reached. +If there are no remaining arguments, +.Ic getopts +will set +.Va var +to the special option, +.Dq -- , +otherwise, it will set +.Va var +to +.Dq \&? . +.Pp +The following code fragment shows how one might process the arguments +for a command that can take the options +.Op a +and +.Op b , +and the option +.Op c , +which requires an argument. +.Pp +.Bd -literal -offset indent +while getopts abc: f +do + case $f in + a | b) flag=$f;; + c) carg=$OPTARG;; + \\?) echo $USAGE; exit 1;; + esac +done +shift `expr $OPTIND - 1` +.Ed +.Pp +This code will accept any of the following as equivalent: +.Pp +.Bd -literal -offset indent +cmd \-acarg file file +cmd \-a \-c arg file file +cmd \-carg -a file file +cmd \-a \-carg \-\- file file +.Ed +.It hash Fl rv Ar command ... +The shell maintains a hash table which remembers the +locations of commands. +With no arguments whatsoever, +the +.Ic hash +command prints out the contents of this table. +Entries which have not been looked at since the last +.Ic cd +command are marked with an asterisk; it is possible for these entries +to be invalid. +.Pp +With arguments, the +.Ic hash +command removes the specified commands from the hash table (unless +they are functions) and then locates them. +With the +.Fl v +option, hash prints the locations of the commands as it finds them. +The +.Fl r +option causes the hash command to delete all the entries in the hash table +except for functions. +.It pwd Op Fl LP +builtin command remembers what the current directory +is rather than recomputing it each time. +This makes it faster. +However, if the current directory is renamed, the builtin version of +.Ic pwd +will continue to print the old name for the directory. +The +.Fl P +option causes the physical value of the current working directory to be shown, +that is, all symbolic links are resolved to their respective values. The +.Fl L +option turns off the effect of any preceding +.Fl P +options. +.It Xo read Op Fl p Ar prompt +.Op Fl r +.Ar variable +.Op Ar ... +.Xc +The prompt is printed if the +.Fl p +option is specified and the standard input is a terminal. +Then a line is read from the standard input. +The trailing newline is deleted from the +line and the line is split as described in the section on word splitting +above, and the pieces are assigned to the variables in order. +At least one variable must be specified. +If there are more pieces than variables, the remaining pieces +(along with the characters in +.Ev IFS +that separated them) are assigned to the last variable. +If there are more variables than pieces, +the remaining variables are assigned the null string. +The +.Ic read +builtin will indicate success unless EOF is encountered on input, in +which case failure is returned. +.Pp +By default, unless the +.Fl r +option is specified, the backslash +.Dq \e +acts as an escape character, causing the following character to be treated +literally. +If a backslash is followed by a newline, the backslash and the +newline will be deleted. +.It readonly Ar name ... +.It readonly Fl p +The specified names are marked as read only, so that they cannot be +subsequently modified or unset. +The shell allows the value of a variable +to be set at the same time it is marked read only by writing +.Pp +.Dl readonly name=value +.Pp +With no arguments the readonly command lists the names of all read only +variables. +With the +.Fl p +option specified the output will be formatted suitably for non-interactive use. +.Pp +.It Xo printf Ar format +.Op Ar arguments ... +.Xc +.Ic printf +formats and prints its arguments, after the first, under control +of the +.Ar format . +The +.Ar format +is a character string which contains three types of objects: plain characters, +which are simply copied to standard output, character escape sequences which +are converted and copied to the standard output, and format specifications, +each of which causes printing of the next successive +.Ar argument . +.Pp +The +.Ar arguments +after the first are treated as strings if the corresponding format is +either +.Cm b , +.Cm c +or +.Cm s ; +otherwise it is evaluated as a C constant, with the following extensions: +.Pp +.Bl -bullet -offset indent -compact +.It +A leading plus or minus sign is allowed. +.It +If the leading character is a single or double quote, the value is the +.Tn ASCII +code of the next character. +.El +.Pp +The format string is reused as often as necessary to satisfy the +.Ar arguments . +Any extra format specifications are evaluated with zero or the null +string. +.Pp +Character escape sequences are in backslash notation as defined in +.St -ansiC . +The characters and their meanings are as follows: +.Bl -tag -width Ds -offset indent +.It Cm \ea +Write a \*[Lt]bell\*[Gt] character. +.It Cm \eb +Write a \*[Lt]backspace\*[Gt] character. +.It Cm \ef +Write a \*[Lt]form-feed\*[Gt] character. +.It Cm \en +Write a \*[Lt]new-line\*[Gt] character. +.It Cm \er +Write a \*[Lt]carriage return\*[Gt] character. +.It Cm \et +Write a \*[Lt]tab\*[Gt] character. +.It Cm \ev +Write a \*[Lt]vertical tab\*[Gt] character. +.It Cm \e\e +Write a backslash character. +.It Cm \e Ns Ar num +Write an 8\-bit character whose +.Tn ASCII +value is the 1\-, 2\-, or 3\-digit +octal number +.Ar num . +.El +.Pp +Each format specification is introduced by the percent character +(``%''). +The remainder of the format specification includes, +in the following order: +.Bl -tag -width Ds +.It "Zero or more of the following flags:" +.Bl -tag -width Ds +.It Cm # +A `#' character +specifying that the value should be printed in an ``alternative form''. +For +.Cm b , +.Cm c , +.Cm d , +and +.Cm s +formats, this option has no effect. +For the +.Cm o +format the precision of the number is increased to force the first +character of the output string to a zero. +For the +.Cm x +.Pq Cm X +format, a non-zero result has the string +.Li 0x +.Pq Li 0X +prepended to it. +For +.Cm e , +.Cm E , +.Cm f , +.Cm g , +and +.Cm G +formats, the result will always contain a decimal point, even if no +digits follow the point (normally, a decimal point only appears in the +results of those formats if a digit follows the decimal point). +For +.Cm g +and +.Cm G +formats, trailing zeros are not removed from the result as they +would otherwise be. +.It Cm \&\- +A minus sign `\-' which specifies +.Em left adjustment +of the output in the indicated field; +.It Cm \&+ +A `+' character specifying that there should always be +a sign placed before the number when using signed formats. +.It Sq \&\ \& +A space specifying that a blank should be left before a positive number +for a signed format. +A `+' overrides a space if both are used; +.It Cm \&0 +A zero `0' character indicating that zero-padding should be used +rather than blank-padding. +A `\-' overrides a `0' if both are used; +.El +.It "Field Width:" +An optional digit string specifying a +.Em field width ; +if the output string has fewer characters than the field width it will +be blank-padded on the left (or right, if the left-adjustment indicator +has been given) to make up the field width (note that a leading zero +is a flag, but an embedded zero is part of a field width); +.It Precision : +An optional period, +.Sq Cm \&.\& , +followed by an optional digit string giving a +.Em precision +which specifies the number of digits to appear after the decimal point, +for +.Cm e +and +.Cm f +formats, or the maximum number of characters to be printed +from a string +.Sm off +.Pf ( Cm b +.Sm on +and +.Cm s +formats); if the digit string is missing, the precision is treated +as zero; +.It Format : +A character which indicates the type of format to use (one of +.Cm diouxXfwEgGbcs ) . +.El +.Pp +A field width or precision may be +.Sq Cm \&* +instead of a digit string. +In this case an +.Ar argument +supplies the field width or precision. +.Pp +The format characters and their meanings are: +.Bl -tag -width Fl +.It Cm diouXx +The +.Ar argument +is printed as a signed decimal (d or i), unsigned octal, unsigned decimal, +or unsigned hexadecimal (X or x), respectively. +.It Cm f +The +.Ar argument +is printed in the style +.Sm off +.Pf [\-]ddd Cm \&. No ddd +.Sm on +where the number of d's +after the decimal point is equal to the precision specification for +the argument. +If the precision is missing, 6 digits are given; if the precision +is explicitly 0, no digits and no decimal point are printed. +.It Cm eE +The +.Ar argument +is printed in the style +.Sm off +.Pf [\-]d Cm \&. No ddd Cm e No \\*(Pmdd +.Sm on +where there +is one digit before the decimal point and the number after is equal to +the precision specification for the argument; when the precision is +missing, 6 digits are produced. +An upper-case E is used for an `E' format. +.It Cm gG +The +.Ar argument +is printed in style +.Cm f +or in style +.Cm e +.Pq Cm E +whichever gives full precision in minimum space. +.It Cm b +Characters from the string +.Ar argument +are printed with backslash-escape sequences expanded. +.br +The following additional backslash-escape sequences are supported: +.Bl -tag -width Ds +.It Cm \ec +Causes +.Nm +to ignore any remaining characters in the string operand containing it, +any remaining string operands, and any additional characters in +the format operand. +.It Cm \e0 Ns Ar num +Write an 8\-bit character whose +.Tn ASCII +value is the 1\-, 2\-, or 3\-digit +octal number +.Ar num . +.El +.It Cm c +The first character of +.Ar argument +is printed. +.It Cm s +Characters from the string +.Ar argument +are printed until the end is reached or until the number of characters +indicated by the precision specification is reached; if the +precision is omitted, all characters in the string are printed. +.It Cm \&% +Print a `%'; no argument is used. +.El +.Pp +In no case does a non-existent or small field width cause truncation of +a field; padding takes place only if the specified field width exceeds +the actual width. +.It Xo set +.Oo { +.Fl options | Cm +options | Cm -- } +.Oc Ar arg ... +.Xc +The +.Ic set +command performs three different functions. +.Pp +With no arguments, it lists the values of all shell variables. +.Pp +If options are given, it sets the specified option +flags, or clears them as described in the section called +.Sx Argument List Processing . +.Pp +The third use of the set command is to set the values of the shell's +positional parameters to the specified args. +To change the positional +parameters without changing any options, use +.Dq -- +as the first argument to set. +If no args are present, the set command +will clear all the positional parameters (equivalent to executing +.Dq shift $# . ) +.It shift Op Ar n +Shift the positional parameters n times. +A +.Ic shift +sets the value of +.Va $1 +to the value of +.Va $2 , +the value of +.Va $2 +to the value of +.Va $3 , +and so on, decreasing +the value of +.Va $# +by one. +If n is greater than the number of positional parameters, +.Ic shift +will issue an error message, and exit with return status 2. +.It test Ar expression +.It \&[ Ar expression Cm ] +The +.Ic test +utility evaluates the expression and, if it evaluates +to true, returns a zero (true) exit status; otherwise +it returns 1 (false). +If there is no expression, test also +returns 1 (false). +.Pp +All operators and flags are separate arguments to the +.Ic test +utility. +.Pp +The following primaries are used to construct expression: +.Bl -tag -width Ar +.It Fl b Ar file +True if +.Ar file +exists and is a block special +file. +.It Fl c Ar file +True if +.Ar file +exists and is a character +special file. +.It Fl d Ar file +True if +.Ar file +exists and is a directory. +.It Fl e Ar file +True if +.Ar file +exists (regardless of type). +.It Fl f Ar file +True if +.Ar file +exists and is a regular file. +.It Fl g Ar file +True if +.Ar file +exists and its set group ID flag +is set. +.It Fl h Ar file +True if +.Ar file +exists and is a symbolic link. +.It Fl k Ar file +True if +.Ar file +exists and its sticky bit is set. +.It Fl n Ar string +True if the length of +.Ar string +is nonzero. +.It Fl p Ar file +True if +.Ar file +is a named pipe +.Po Tn FIFO Pc . +.It Fl r Ar file +True if +.Ar file +exists and is readable. +.It Fl s Ar file +True if +.Ar file +exists and has a size greater +than zero. +.It Fl t Ar file_descriptor +True if the file whose file descriptor number +is +.Ar file_descriptor +is open and is associated with a terminal. +.It Fl u Ar file +True if +.Ar file +exists and its set user ID flag +is set. +.It Fl w Ar file +True if +.Ar file +exists and is writable. +True +indicates only that the write flag is on. +The file is not writable on a read-only file +system even if this test indicates true. +.It Fl x Ar file +True if +.Ar file +exists and is executable. +True +indicates only that the execute flag is on. +If +.Ar file +is a directory, true indicates that +.Ar file +can be searched. +.It Fl z Ar string +True if the length of +.Ar string +is zero. +.It Fl L Ar file +True if +.Ar file +exists and is a symbolic link. +This operator is retained for compatibility with previous versions of +this program. +Do not rely on its existence; use +.Fl h +instead. +.It Fl O Ar file +True if +.Ar file +exists and its owner matches the effective user id of this process. +.It Fl G Ar file +True if +.Ar file +exists and its group matches the effective group id of this process. +.It Fl S Ar file +True if +.Ar file +exists and is a socket. +.It Ar file1 Fl nt Ar file2 +True if +.Ar file1 +exists and is newer than +.Ar file2 . +.It Ar file1 Fl ot Ar file2 +True if +.Ar file1 +exists and is older than +.Ar file2 . +.It Ar file1 Fl ef Ar file2 +True if +.Ar file1 +and +.Ar file2 +exist and refer to the same file. +.It Ar string +True if +.Ar string +is not the null +string. +.It Ar \&s\&1 Cm \&= Ar \&s\&2 +True if the strings +.Ar \&s\&1 +and +.Ar \&s\&2 +are identical. +.It Ar \&s\&1 Cm \&!= Ar \&s\&2 +True if the strings +.Ar \&s\&1 +and +.Ar \&s\&2 +are not identical. +.It Ar \&s\&1 Cm \&\*[Lt] Ar \&s\&2 +True if string +.Ar \&s\&1 +comes before +.Ar \&s\&2 +based on the ASCII value of their characters. +.It Ar \&s\&1 Cm \&\*[Gt] Ar \&s\&2 +True if string +.Ar \&s\&1 +comes after +.Ar \&s\&2 +based on the ASCII value of their characters. +.It Ar \&n\&1 Fl \&eq Ar \&n\&2 +True if the integers +.Ar \&n\&1 +and +.Ar \&n\&2 +are algebraically +equal. +.It Ar \&n\&1 Fl \&ne Ar \&n\&2 +True if the integers +.Ar \&n\&1 +and +.Ar \&n\&2 +are not +algebraically equal. +.It Ar \&n\&1 Fl \> Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically +greater than the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \&ge Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically +greater than or equal to the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \< Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically less +than the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \&le Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically less +than or equal to the integer +.Ar \&n\&2 . +.El +.Pp +These primaries can be combined with the following operators: +.Bl -tag -width Ar +.It Cm \&! Ar expression +True if +.Ar expression +is false. +.It Ar expression1 Fl a Ar expression2 +True if both +.Ar expression1 +and +.Ar expression2 +are true. +.It Ar expression1 Fl o Ar expression2 +True if either +.Ar expression1 +or +.Ar expression2 +are true. +.It Cm \&( Ns Ar expression Ns Cm \&) +True if expression is true. +.El +.Pp +The +.Fl a +operator has higher precedence than the +.Fl o +operator. +.It times +Print the accumulated user and system times for the shell and for processes +run from the shell. The return status is 0. +.It Xo trap +.Op Ar action Ar signal ... +.Xc +Cause the shell to parse and execute action when any of the specified +signals are received. +The signals are specified by signal number or as the name of the signal. +If +.Ar signal +is +.Li 0 , +the action is executed when the shell exits. +.Ar action +may be null, which cause the specified signals to be ignored. +With +.Ar action +omitted or set to `-' the specified signals are set to their default action. +When the shell forks off a subshell, it resets trapped (but not ignored) +signals to the default action. +The +.Ic trap +command has no effect on signals that were +ignored on entry to the shell. +.Ic trap +without any arguments cause it to write a list of signals and their +associated action to the standard output in a format that is suitable +as an input to the shell that achieves the same trapping results. +.Pp +Examples: +.Pp +.Dl trap +.Pp +List trapped signals and their corresponding action +.Pp +.Dl trap '' INT QUIT tstp 30 +.Pp +Ignore signals INT QUIT TSTP USR1 +.Pp +.Dl trap date INT +.Pp +Print date upon receiving signal INT +.It type Op Ar name ... +Interpret each name as a command and print the resolution of the command +search. +Possible resolutions are: +shell keyword, alias, shell builtin, +command, tracked alias and not found. +For aliases the alias expansion is +printed; for commands and tracked aliases the complete pathname of the +command is printed. +.It ulimit Xo +.Op Fl H \*(Ba Fl S +.Op Fl a \*(Ba Fl tfdscmlpn Op Ar value +.Xc +Inquire about or set the hard or soft limits on processes or set new +limits. +The choice between hard limit (which no process is allowed to +violate, and which may not be raised once it has been lowered) and soft +limit (which causes processes to be signaled but not necessarily killed, +and which may be raised) is made with these flags: +.Bl -tag -width Fl +.It Fl H +set or inquire about hard limits +.It Fl S +set or inquire about soft limits. +If neither +.Fl H +nor +.Fl S +is specified, the soft limit is displayed or both limits are set. +If both are specified, the last one wins. +.El +.Pp +.Bl -tag -width Fl +The limit to be interrogated or set, then, is chosen by specifying +any one of these flags: +.It Fl a +show all the current limits +.It Fl t +show or set the limit on CPU time (in seconds) +.It Fl f +show or set the limit on the largest file that can be created +(in 512-byte blocks) +.It Fl d +show or set the limit on the data segment size of a process (in kilobytes) +.It Fl s +show or set the limit on the stack size of a process (in kilobytes) +.It Fl c +show or set the limit on the largest core dump size that can be produced +(in 512-byte blocks) +.It Fl m +show or set the limit on the total physical memory that can be +in use by a process (in kilobytes) +.It Fl l +show or set the limit on how much memory a process can lock with +.Xr mlock 2 +(in kilobytes) +.It Fl p +show or set the limit on the number of processes this user can +have at one time +.It Fl n +show or set the limit on the number files a process can have open at once +.El +.Pp +If none of these is specified, it is the limit on file size that is shown +or set. +If value is specified, the limit is set to that number; otherwise +the current limit is displayed. +.Pp +Limits of an arbitrary process can be displayed or set using the +.Xr sysctl 8 +utility. +.Pp +.It umask Op Ar mask +Set the value of umask (see +.Xr umask 2 ) +to the specified octal value. +If the argument is omitted, the umask value is printed. +.It unalias Xo +.Op Fl a +.Op Ar name +.Xc +If +.Ar name +is specified, the shell removes that alias. +If +.Fl a +is specified, all aliases are removed. +.It unset Xo +.Op Fl fv +.Ar name ... +.Xc +The specified variables and functions are unset and unexported. +If +.Fl f +or +.Fl v +is specified, the corresponding function or variable is unset, respectively. +If a given name corresponds to both a variable and a function, and no +options are given, only the variable is unset. +.It wait Op Ar job +Wait for the specified job to complete and return the exit status of the +last process in the job. +If the argument is omitted, wait for all jobs to +complete and the return an exit status of zero. +.El +.Ss Command Line Editing +When +.Nm +is being used interactively from a terminal, the current command +and the command history (see +.Ic fc +in +.Sx Builtins ) +can be edited using vi-mode command-line editing. +This mode uses commands, described below, +similar to a subset of those described in the vi man page. +The command +.Ql set -o vi +enables vi-mode editing and place sh into vi insert mode. +With vi-mode +enabled, sh can be switched between insert mode and command mode. +The editor is not described in full here, but will be in a later document. +It's similar to vi: typing +.Aq ESC +will throw you into command VI command mode. +Hitting +.Aq return +while in command mode will pass the line to the shell. +.Sh EXIT STATUS +Errors that are detected by the shell, such as a syntax error, will cause the +shell to exit with a non-zero exit status. +If the shell is not an +interactive shell, the execution of the shell file will be aborted. +Otherwise +the shell will return the exit status of the last command executed, or +if the exit builtin is used with a numeric argument, it will return the +argument. +.Sh ENVIRONMENT +.Bl -tag -width MAILCHECK +.It Ev HOME +Set automatically by +.Xr login 1 +from the user's login directory in the password file +.Pq Xr passwd 4 . +This environment variable also functions as the default argument for the +cd builtin. +.It Ev PATH +The default search path for executables. +See the above section +.Sx Path Search . +.It Ev CDPATH +The search path used with the cd builtin. +.It Ev MAIL +The name of a mail file, that will be checked for the arrival of new mail. +Overridden by +.Ev MAILPATH . +.It Ev MAILCHECK +The frequency in seconds that the shell checks for the arrival of mail +in the files specified by the +.Ev MAILPATH +or the +.Ev MAIL +file. +If set to 0, the check will occur at each prompt. +.It Ev MAILPATH +A colon +.Dq \&: +separated list of file names, for the shell to check for incoming mail. +This environment setting overrides the +.Ev MAIL +setting. +There is a maximum of 10 mailboxes that can be monitored at once. +.It Ev PS1 +The primary prompt string, which defaults to +.Dq $ \ , +unless you are the superuser, in which case it defaults to +.Dq # \ . +.It Ev PS2 +The secondary prompt string, which defaults to +.Dq \*[Gt] \ . +.It Ev PS4 +Output before each line when execution trace (set -x) is enabled, +defaults to +.Dq + \ . +.It Ev IFS +Input Field Separators. +This is normally set to +.Aq space , +.Aq tab , +and +.Aq newline . +See the +.Sx White Space Splitting +section for more details. +.It Ev TERM +The default terminal setting for the shell. +This is inherited by +children of the shell, and is used in the history editing modes. +.It Ev HISTSIZE +The number of lines in the history buffer for the shell. +.It Ev PWD +The logical value of the current working directory. This is set by the +.Ic cd +command. +.It Ev OLDPWD +The previous logical value of the current working directory. This is set by +the +.Ic cd +command. +.It Ev PPID +The process ID of the parent process of the shell. +.El +.Sh FILES +.Bl -item -width HOMEprofilexxxx +.It +.Pa $HOME/.profile +.It +.Pa /etc/profile +.El +.Sh SEE ALSO +.Xr csh 1 , +.Xr echo 1 , +.Xr getopt 1 , +.Xr ksh 1 , +.Xr login 1 , +.Xr printf 1 , +.Xr test 1 , +.Xr getopt 3 , +.Xr passwd 5 , +.\" .Xr profile 4 , +.Xr environ 7 , +.Xr sysctl 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . +It was, however, unmaintainable so we wrote this one. +.Sh BUGS +Setuid shell scripts should be avoided at all costs, as they are a +significant security risk. +.Pp +PS1, PS2, and PS4 should be subject to parameter expansion before +being displayed. diff --git a/usr/dash/shell.h b/usr/dash/shell.h new file mode 100644 index 0000000..98edc8b --- /dev/null +++ b/usr/dash/shell.h @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)shell.h 8.2 (Berkeley) 5/4/95 + */ + +/* + * The follow should be set to reflect the type of system you have: + * JOBS -> 1 if you have Berkeley job control, 0 otherwise. + * SHORTNAMES -> 1 if your linker cannot handle long names. + * define BSD if you are running 4.2 BSD or later. + * define SYSV if you are running under System V. + * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) + * define DEBUG=2 to compile in and turn on debugging. + * define DO_SHAREDVFORK to indicate that vfork(2) shares its address + * with its parent. + * + * When debugging is on, debugging info will be written to ./trace and + * a quit signal will generate a core dump. + */ + +#include <sys/param.h> + +#ifndef JOBS +#define JOBS 1 +#endif +#ifndef BSD +#define BSD 1 +#endif + +#ifndef DO_SHAREDVFORK +#if __NetBSD_Version__ >= 104000000 +#define DO_SHAREDVFORK +#endif +#endif + +typedef void *pointer; +#ifndef NULL +#define NULL (void *)0 +#endif +#define STATIC static +#define MKINIT /* empty */ + +extern char nullstr[1]; /* null string */ + + +#ifdef DEBUG +#define TRACE(param) trace param +#define TRACEV(param) tracev param +#else +#define TRACE(param) +#define TRACEV(param) +#endif + +#if defined(__GNUC__) && __GNUC__ < 3 +#define va_copy __va_copy +#endif + +#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) +#define __builtin_expect(x, expected_value) (x) +#endif + +#define likely(x) __builtin_expect(!!(x),1) +#define unlikely(x) __builtin_expect(!!(x),0) + +/* + * Hack to calculate maximum length. + * (length * 8 - 1) * log10(2) + 1 + 1 + 12 + * The second 1 is for the minus sign and the 12 is a safety margin. + */ +static inline int max_int_length(int bytes) +{ + return (bytes * 8 - 1) * 0.30102999566398119521 + 14; +} diff --git a/usr/dash/show.c b/usr/dash/show.c new file mode 100644 index 0000000..4a049e9 --- /dev/null +++ b/usr/dash/show.c @@ -0,0 +1,406 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <stdio.h> +#include <stdarg.h> + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "mystring.h" +#include "show.h" +#include "options.h" + + +#ifdef DEBUG +static void shtree(union node *, int, char *, FILE*); +static void shcmd(union node *, FILE *); +static void sharg(union node *, FILE *); +static void indent(int, char *, FILE *); +static void trstring(char *); + + +void +showtree(union node *n) +{ + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static void +shtree(union node *n, int ind, char *pfx, FILE *fp) +{ + struct nodelist *lp; + const char *s; + + if (n == NULL) + return; + + indent(ind, pfx, fp); + switch(n->type) { + case NSEMI: + s = "; "; + goto binop; + case NAND: + s = " && "; + goto binop; + case NOR: + s = " || "; +binop: + shtree(n->nbinary.ch1, ind, NULL, fp); + /* if (ind < 0) */ + fputs(s, fp); + shtree(n->nbinary.ch2, ind, NULL, fp); + break; + case NCMD: + shcmd(n, fp); + if (ind >= 0) + putc('\n', fp); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + shcmd(lp->n, fp); + if (lp->next) + fputs(" | ", fp); + } + if (n->npipe.backgnd) + fputs(" &", fp); + if (ind >= 0) + putc('\n', fp); + break; + default: + fprintf(fp, "<node type %d>", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + + +static void +shcmd(union node *cmd, FILE *fp) +{ + union node *np; + int first; + const char *s; + int dftfd; + + first = 1; + for (np = cmd->ncmd.args ; np ; np = np->narg.next) { + if (! first) + putchar(' '); + sharg(np, fp); + first = 0; + } + for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { + if (! first) + putchar(' '); + switch (np->nfile.type) { + case NTO: s = ">"; dftfd = 1; break; + case NCLOBBER: s = ">|"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NFROM: s = "<"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + case NFROMTO: s = "<>"; dftfd = 0; break; + default: s = "*error*"; dftfd = 0; break; + } + if (np->nfile.fd != dftfd) + fprintf(fp, "%d", np->nfile.fd); + fputs(s, fp); + if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { + fprintf(fp, "%d", np->ndup.dupfd); + } else { + sharg(np->nfile.fname, fp); + } + first = 0; + } +} + + + +static void +sharg(union node *arg, FILE *fp) +{ + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + printf("<node type %d>\n", arg->type); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch ((signed char)*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + if (subtype == VSLENGTH) + putc('#', fp); + + while (*p != '=') + putc(*p++, fp); + + if (subtype & VSNUL) + putc(':', fp); + + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + case VSTRIMLEFT: + putc('#', fp); + break; + case VSTRIMLEFTMAX: + putc('#', fp); + putc('#', fp); + break; + case VSTRIMRIGHT: + putc('%', fp); + break; + case VSTRIMRIGHTMAX: + putc('%', fp); + putc('%', fp); + break; + case VSLENGTH: + break; + default: + printf("<subtype %d>", subtype); + } + break; + case CTLENDVAR: + putc('}', fp); + break; + case CTLBACKQ: + putc('$', fp); + putc('(', fp); + shtree(bqlist->n, -1, NULL, fp); + putc(')', fp); + break; + default: + putc(*p, fp); + break; + } + } +} + + +static void +indent(int amount, char *pfx, FILE *fp) +{ + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} + + + +/* + * Debugging stuff. + */ + + +FILE *tracefile; + + +void +trputc(int c) +{ + if (debug != 1) + return; + putc(c, tracefile); +} + +void +trace(const char *fmt, ...) +{ + va_list va; + + if (debug != 1) + return; + va_start(va, fmt); + (void) vfprintf(tracefile, fmt, va); + va_end(va); +} + +void +tracev(const char *fmt, va_list va) +{ + if (debug != 1) + return; + (void) vfprintf(tracefile, fmt, va); +} + + +void +trputs(const char *s) +{ + if (debug != 1) + return; + fputs(s, tracefile); +} + + +static void +trstring(char *s) +{ + char *p; + char c; + + if (debug != 1) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch ((signed char)*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +} + + +void +trargs(char **ap) +{ + if (debug != 1) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } +} + + +void +opentrace(void) +{ + char s[100]; +#ifdef O_APPEND + int flags; +#endif + + if (debug != 1) { + if (tracefile) + fflush(tracefile); + /* leave open because libedit might be using it */ + return; + } +#ifdef not_this_way + { + char *p; + if ((p = getenv(homestr)) == NULL) { + if (geteuid() == 0) + p = "/"; + else + p = "/tmp"; + } + scopy(p, s); + strcat(s, "/trace"); + } +#else + scopy("./trace", s); +#endif /* not_this_way */ + if (tracefile) { +#ifndef __KLIBC__ + if (!freopen(s, "a", tracefile)) { +#else + if (!(!fclose(tracefile) && (tracefile = fopen(s, "a")))) { +#endif /* __KLIBC__ */ + fprintf(stderr, "Can't re-open %s\n", s); + debug = 0; + return; + } + } else { + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + debug = 0; + return; + } + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif +#ifndef __KLIBC__ + setlinebuf(tracefile); +#endif /* __KLIBC__ */ + fputs("\nTracing started.\n", tracefile); +} +#endif /* DEBUG */ diff --git a/usr/dash/show.h b/usr/dash/show.h new file mode 100644 index 0000000..d0ccac7 --- /dev/null +++ b/usr/dash/show.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. 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. + * 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. + * + * @(#)show.h 1.1 (Berkeley) 5/4/95 + */ + +#include <stdarg.h> + +#ifdef DEBUG +union node; +void showtree(union node *); +void trace(const char *, ...); +void tracev(const char *, va_list); +void trargs(char **); +void trputc(int); +void trputs(const char *); +void opentrace(void); +#endif diff --git a/usr/dash/system.c b/usr/dash/system.c new file mode 100644 index 0000000..aa1df8a --- /dev/null +++ b/usr/dash/system.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2004 + * Herbert Xu <herbert@gondor.apana.org.au>. 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. + * 3. 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 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. + */ + +#ifndef HAVE_ISALPHA +#define isalnum _isalnum +#define iscntrl _iscntrl +#define islower _islower +#define isspace _isspace +#define isalpha _isalpha +#define isdigit _isdigit +#define isprint _isprint +#define isupper _isupper +#define isblank _isblank +#define isgraph _isgraph +#define ispunct _ispunct +#define isxdigit _isxdigit +#include <ctype.h> +#undef isalnum +#undef iscntrl +#undef islower +#undef isspace +#undef isalpha +#undef isdigit +#undef isprint +#undef isupper +#undef isblank +#undef isgraph +#undef ispunct +#undef isxdigit +#endif + +#include <signal.h> +#include <string.h> + +#include "error.h" +#include "output.h" +#include "system.h" + +#ifndef HAVE_MEMPCPY +void *mempcpy(void *dest, const void *src, size_t n) +{ + return memcpy(dest, src, n) + n; +} +#endif + +#ifndef HAVE_STPCPY +char *stpcpy(char *dest, const char *src) +{ + size_t len = strlen(src); + dest[len] = 0; + return mempcpy(dest, src, len); +} +#endif + +#ifndef HAVE_STRCHRNUL +char *strchrnul(const char *s, int c) +{ + char *p = strchr(s, c); + if (!p) + p = (char *)s + strlen(s); + return p; +} +#endif + +#ifndef HAVE_STRSIGNAL +char *strsignal(int sig) +{ + static char buf[19]; + + if ((unsigned)sig < NSIG && sys_siglist[sig]) + return (char *)sys_siglist[sig]; + fmtstr(buf, sizeof(buf), "Signal %d", sig); + return buf; +} +#endif + +#ifndef HAVE_BSEARCH +void *bsearch(const void *key, const void *base, size_t nmemb, + size_t size, int (*cmp)(const void *, const void *)) +{ + while (nmemb) { + size_t mididx = nmemb / 2; + const void *midobj = base + mididx * size; + int diff = cmp(key, midobj); + + if (diff == 0) + return (void *)midobj; + + if (diff > 0) { + base = midobj + size; + nmemb -= mididx + 1; + } else + nmemb = mididx; + } + + return 0; +} +#endif + +#ifndef HAVE_SYSCONF +long sysconf(int name) +{ + sh_error("no sysconf for: %d", name); +} +#endif + +#ifndef HAVE_ISALPHA +int isalnum(int c) { + return _isalnum(c); +} + + +int iscntrl(int c) { + return _iscntrl(c); +} + + +int islower(int c) { + return _islower(c); +} + + +int isspace(int c) { + return _isspace(c); +} + + +int isalpha(int c) { + return _isalpha(c); +} + + +int isdigit(int c) { + return _isdigit(c); +} + + +int isprint(int c) { + return _isprint(c); +} + + +int isupper(int c) { + return _isupper(c); +} + + +#if HAVE_DECL_ISBLANK +int isblank(int c) { + return _isblank(c); +} +#endif + + +int isgraph(int c) { + return _isgraph(c); +} + + +int ispunct(int c) { + return _ispunct(c); +} + + +int isxdigit(int c) { + return _isxdigit(c); +} +#endif + +#if !HAVE_DECL_ISBLANK +int isblank(int c) { + return c == ' ' || c == '\t'; +} +#endif diff --git a/usr/dash/system.h b/usr/dash/system.h new file mode 100644 index 0000000..c8424f7 --- /dev/null +++ b/usr/dash/system.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2004 + * Herbert Xu <herbert@gondor.apana.org.au>. 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. + * 3. 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 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. + */ + +#include <limits.h> +#include <signal.h> +#include <sys/types.h> + +#ifndef SSIZE_MAX +#define SSIZE_MAX ((ssize_t)((size_t)-1 >> 1)) +#endif + +static inline void sigclearmask(void) +{ +#ifdef HAVE_SIGSETMASK + sigsetmask(0); +#else + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, 0); +#endif +} + +#ifndef HAVE_MEMPCPY +void *mempcpy(void *, const void *, size_t); +#endif + +#ifndef HAVE_STPCPY +char *stpcpy(char *, const char *); +#endif + +#ifndef HAVE_STRCHRNUL +char *strchrnul(const char *, int); +#endif + +#ifndef HAVE_STRSIGNAL +char *strsignal(int); +#endif + +#ifndef HAVE_STRTOIMAX +#define strtoimax strtoll +#endif + +#ifndef HAVE_STRTOUMAX +#define strtoumax strtoull +#endif + +#ifndef HAVE_BSEARCH +void *bsearch(const void *, const void *, size_t, size_t, + int (*)(const void *, const void *)); +#endif + +#ifndef HAVE_KILLPG +static inline int killpg(pid_t pid, int signal) +{ +#ifdef DEBUG + if (pid < 0) + abort(); +#endif + return kill(-pid, signal); +} +#endif + +#ifndef HAVE_SYSCONF +#define _SC_CLK_TCK 2 +long sysconf(int) __attribute__((__noreturn__)); +#endif + +#if !HAVE_DECL_ISBLANK +int isblank(int c); +#endif + +/* + * A trick to suppress uninitialized variable warning without generating any + * code + */ +#define uninitialized_var(x) x = x diff --git a/usr/dash/trap.c b/usr/dash/trap.c new file mode 100644 index 0000000..1e2a867 --- /dev/null +++ b/usr/dash/trap.c @@ -0,0 +1,467 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "shell.h" +#include "main.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" +#include "jobs.h" +#include "show.h" +#include "options.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "trap.h" +#include "mystring.h" + +#ifdef HETIO +#include "hetio.h" +#endif + +/* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell, + */ + +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ + + +/* trap handler commands */ +static char *trap[NSIG]; +/* number of non-null traps */ +int trapcnt; +/* current value of signal */ +char sigmode[NSIG - 1]; +/* indicates specified signal received */ +static char gotsig[NSIG - 1]; +/* last pending signal */ +volatile sig_atomic_t pendingsigs; +/* received SIGCHLD */ +int gotsigchld; + +#ifdef mkinit +INCLUDE "trap.h" +INIT { + sigmode[SIGCHLD - 1] = S_DFL; + setsignal(SIGCHLD); +} +#endif + +/* + * The trap builtin. + */ + +int +trapcmd(int argc, char **argv) +{ + char *action; + char **ap; + int signo; + + nextopt(nullstr); + ap = argptr; + if (!*ap) { + for (signo = 0 ; signo < NSIG ; signo++) { + if (trap[signo] != NULL) { + out1fmt( + "trap -- %s %s\n", + single_quote(trap[signo]), + signal_name(signo) + ); + } + } + return 0; + } + if (!ap[1]) + action = NULL; + else + action = *ap++; + while (*ap) { + if ((signo = decode_signal(*ap, 0)) < 0) { + outfmt(out2, "trap: %s: bad trap\n", *ap); + return 1; + } + INTOFF; + if (action) { + if (action[0] == '-' && action[1] == '\0') + action = NULL; + else { + if (*action) + trapcnt++; + action = savestr(action); + } + } + if (trap[signo]) { + if (*trap[signo]) + trapcnt--; + ckfree(trap[signo]); + } + trap[signo] = action; + if (signo != 0) + setsignal(signo); + INTON; + ap++; + } + return 0; +} + + + +/* + * Clear traps on a fork. + */ + +void +clear_traps(void) +{ + char **tp; + + INTOFF; + for (tp = trap ; tp < &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + } + } + trapcnt = 0; + INTON; +} + + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +void +setsignal(int signo) +{ + int action; + char *t, tsig; + struct sigaction act; + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (rootshell && action == S_DFL) { + switch (signo) { + case SIGINT: + if (iflag || minusc || sflag == 0) + action = S_CATCH; + break; + case SIGQUIT: +#ifdef DEBUG + if (debug) + break; +#endif + /* FALLTHROUGH */ + case SIGTERM: + if (iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (mflag) + action = S_IGN; + break; +#endif + } + } + + if (signo == SIGCHLD) + action = S_CATCH; + + t = &sigmode[signo - 1]; + tsig = *t; + if (tsig == 0) { + /* + * current setting unknown + */ + if (sigaction(signo, 0, &act) == -1) { + /* + * Pretend it worked; maybe we should give a warning + * here, but other shells don't. We don't alter + * sigmode, so that we retry every time. + */ + return; + } + if (act.sa_handler == SIG_IGN) { + if (mflag && (signo == SIGTSTP || + signo == SIGTTIN || signo == SIGTTOU)) { + tsig = S_IGN; /* don't hard ignore these */ + } else + tsig = S_HARD_IGN; + } else { + tsig = S_RESET; /* force to be set */ + } + } + if (tsig == S_HARD_IGN || tsig == action) + return; + switch (action) { + case S_CATCH: + act.sa_handler = onsig; + break; + case S_IGN: + act.sa_handler = SIG_IGN; + break; + default: + act.sa_handler = SIG_DFL; + } + *t = action; + act.sa_flags = 0; + sigfillset(&act.sa_mask); + sigaction(signo, &act, 0); +} + +/* + * Ignore a signal. + */ + +void +ignoresig(int signo) +{ + if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { + signal(signo, SIG_IGN); + } + sigmode[signo - 1] = S_HARD_IGN; +} + + + +/* + * Signal handler. + */ + +void +onsig(int signo) +{ + if (signo == SIGCHLD) { + gotsigchld = 1; + if (!trap[SIGCHLD]) + return; + } + + gotsig[signo - 1] = 1; + pendingsigs = signo; + + if (signo == SIGINT && !trap[SIGINT]) { + if (!suppressint) + onint(); + intpending = 1; + } +} + + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +void dotrap(void) +{ + char *p; + char *q; + int i; + int savestatus; + + savestatus = exitstatus; + pendingsigs = 0; + barrier(); + + for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) { + if (!*q) + continue; + *q = 0; + + p = trap[i + 1]; + if (!p) + continue; + evalstring(p, 0); + exitstatus = savestatus; + if (evalskip) + break; + } +} + + + +/* + * Controls whether the shell is interactive or not. + */ + + +void +setinteractive(int on) +{ + static int is_interactive; + + if (++on == is_interactive) + return; + is_interactive = on; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); +} + + + +/* + * Called to exit the shell. + */ + +void +exitshell(void) +{ + struct jmploc loc; + char *p; + volatile int status; + +#ifdef HETIO + hetio_reset_term(); +#endif + status = exitstatus; + TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); + if (setjmp(loc.loc)) { + if (exception == EXEXIT) + status = exitstatus; + goto out; + } + handler = &loc; + if ((p = trap[0])) { + trap[0] = NULL; + evalskip = 0; + evalstring(p, 0); + } +out: + /* + * Disable job control so that whoever had the foreground before we + * started can get it back. + */ + if (likely(!setjmp(loc.loc))) + setjobctl(0); + flushall(); + _exit(status); + /* NOTREACHED */ +} + +/* + * Decode a signal name + */ +int decode_signal(const char *string, int minsig) +{ + int i; + + if (is_number(string)) { + i = atoi(string); + if (i >= NSIG) { + return -1; + } + return i; + } + + for ( i = minsig ; i < NSIG ; i++ ) { + if ( sys_sigabbrev[i] && + !strcasecmp(string, sys_sigabbrev[i]) ) + return i; + } + +#ifdef SIGRTMIN + if ( !strncasecmp(string, "RTMIN", 5) ) { + char *ep; + + if ( string[5] && string[5] != '+' ) + return -1; + i = SIGRTMIN + strtol(string+5, &ep, 10); + if ( *ep || i < SIGRTMIN || i > SIGRTMAX ) + return -1; + return i; + } + + if ( !strncasecmp(string, "RTMAX", 5) ) { + char *ep; + + if ( string[5] && string[5] != '-' ) + return -1; + i = SIGRTMAX + strtol(string+5, &ep, 10); + if ( *ep || i < SIGRTMIN || i > SIGRTMAX ) + return -1; + return i; + } +#endif + + return -1; +} + +/* + * Human-readable signal name + */ +const char * +signal_name(int sig) +{ + static char buf[64]; + + if ( sig < 0 || sig >= NSIG ) { + return NULL; + } else if ( sys_sigabbrev[sig] ) { + return sys_sigabbrev[sig]; +#ifdef SIGRTMIN + } else if ( sig >= SIGRTMIN && sig <= SIGRTMAX ) { + snprintf(buf, sizeof buf, "RTMIN+%d", sig-SIGRTMIN); + return buf; +#endif + } else { + snprintf(buf, sizeof buf, "%d", sig); + return buf; + } +} diff --git a/usr/dash/trap.h b/usr/dash/trap.h new file mode 100644 index 0000000..6590be1 --- /dev/null +++ b/usr/dash/trap.h @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)trap.h 8.3 (Berkeley) 6/5/95 + */ + +#include <signal.h> + +extern int trapcnt; +extern char sigmode[]; +extern volatile sig_atomic_t pendingsigs; +extern int gotsigchld; + +int trapcmd(int, char **); +void clear_traps(void); +void setsignal(int); +void ignoresig(int); +void onsig(int); +void dotrap(void); +void setinteractive(int); +void exitshell(void) __attribute__((__noreturn__)); +int decode_signal(const char *, int); +const char *signal_name(int); + +static inline int have_traps(void) +{ + return trapcnt; +} diff --git a/usr/dash/var.c b/usr/dash/var.c new file mode 100644 index 0000000..dc90249 --- /dev/null +++ b/usr/dash/var.c @@ -0,0 +1,710 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +/* + * Shell variables. + */ + +#include "shell.h" +#include "output.h" +#include "expand.h" +#include "nodes.h" /* for other headers */ +#include "exec.h" +#include "syntax.h" +#include "options.h" +#include "mail.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "parser.h" +#include "show.h" +#ifndef SMALL +#include "myhistedit.h" +#endif +#include "system.h" + + +#define VTABSIZE 39 + + +struct localvar_list { + struct localvar_list *next; + struct localvar *lv; +}; + +MKINIT struct localvar_list *localvar_stack; + +const char defpathvar[] = + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; +#ifdef IFS_BROKEN +const char defifsvar[] = "IFS= \t\n"; +#else +const char defifs[] = " \t\n"; +#endif + +int lineno; +char linenovar[sizeof("LINENO=")+sizeof(int)*CHAR_BIT/3+1] = "LINENO="; + +/* Some macros in var.h depend on the order, add new variables to the end. */ +struct var varinit[] = { +#if ATTY + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY\0", 0 }, +#endif +#ifdef IFS_BROKEN + { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 }, +#else + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 }, +#endif + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail }, + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, + { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath }, + { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 }, + { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, + { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, + { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, +#ifdef WITH_LINENO + { 0, VSTRFIXED|VTEXTFIXED, linenovar, 0 }, +#endif +#ifndef SMALL + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM\0", 0 }, + { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE\0", sethistsize }, +#endif +}; + +STATIC struct var *vartab[VTABSIZE]; + +STATIC struct var **hashvar(const char *); +STATIC int vpcmp(const void *, const void *); +STATIC struct var **findvar(struct var **, const char *); + +/* + * Initialize the varable symbol tables and import the environment + */ + +#ifdef mkinit +INCLUDE <unistd.h> +INCLUDE <sys/types.h> +INCLUDE <sys/stat.h> +INCLUDE "cd.h" +INCLUDE "output.h" +INCLUDE "var.h" +MKINIT char **environ; +INIT { + char **envp; + static char ppid[32] = "PPID="; + const char *p; + struct stat st1, st2; + + initvar(); + for (envp = environ ; *envp ; envp++) { + p = endofname(*envp); + if (p != *envp && *p == '=') { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } + + fmtstr(ppid + 5, sizeof(ppid) - 5, "%ld", (long) getppid()); + setvareq(ppid, VTEXTFIXED); + + p = lookupvar("PWD"); + if (p) + if (*p != '/' || stat(p, &st1) || stat(".", &st2) || + st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) + p = 0; + setpwd(p, 0); +} + +RESET { + unwindlocalvars(0); +} +#endif + + +/* + * This routine initializes the builtin variables. It is called when the + * shell is initialized. + */ + +void +initvar(void) +{ + struct var *vp; + struct var *end; + struct var **vpp; + + vp = varinit; + end = vp + sizeof(varinit) / sizeof(varinit[0]); + do { + vpp = hashvar(vp->text); + vp->next = *vpp; + *vpp = vp; + } while (++vp < end); + /* + * PS1 depends on uid + */ + if (!geteuid()) + vps1.text = "PS1=# "; +} + +/* + * Set the value of a variable. The flags argument is ored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +struct var *setvar(const char *name, const char *val, int flags) +{ + char *p, *q; + size_t namelen; + char *nameeq; + size_t vallen; + struct var *vp; + + q = endofname(name); + p = strchrnul(q, '='); + namelen = p - name; + if (!namelen || p != q) + sh_error("%.*s: bad variable name", namelen, name); + vallen = 0; + if (val == NULL) { + flags |= VUNSET; + } else { + vallen = strlen(val); + } + INTOFF; + p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen); + if (val) { + *p++ = '='; + p = mempcpy(p, val, vallen); + } + *p = '\0'; + vp = setvareq(nameeq, flags | VNOSAVE); + INTON; + + return vp; +} + +/* + * Set the given integer as the value of a variable. The flags argument is + * ored with the flags of the variable. + */ + +intmax_t setvarint(const char *name, intmax_t val, int flags) +{ + int len = max_int_length(sizeof(val)); + char buf[len]; + + fmtstr(buf, len, "%" PRIdMAX, val); + setvar(name, buf, flags); + return val; +} + + + +/* + * Same as setvar except that the variable and value are passed in + * the first argument as name=value. Since the first argument will + * be actually stored in the table, it should not be a string that + * will go away. + * Called with interrupts off. + */ + +struct var *setvareq(char *s, int flags) +{ + struct var *vp, **vpp; + + vpp = hashvar(s); + flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); + vpp = findvar(vpp, s); + vp = *vpp; + if (vp) { + if (vp->flags & VREADONLY) { + const char *n; + + if (flags & VNOSAVE) + free(s); + n = vp->text; + sh_error("%.*s: is read only", strchrnul(n, '=') - n, + n); + } + + if (flags & VNOSET) + goto out; + + if (vp->func && (flags & VNOFUNC) == 0) + (*vp->func)(strchrnul(s, '=') + 1); + + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + + if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | + (vp->flags & VSTRFIXED)) == VUNSET) { + *vpp = vp->next; + ckfree(vp); +out_free: + if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE) + ckfree(s); + goto out; + } + + flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); + } else { + if (flags & VNOSET) + goto out; + if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET) + goto out_free; + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->next = *vpp; + vp->func = NULL; + *vpp = vp; + } + if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) + s = savestr(s); + vp->text = s; + vp->flags = flags; + +out: + return vp; +} + + + +/* + * Process a linked list of variable assignments. + */ + +void +listsetvar(struct strlist *list, int flags) +{ + struct strlist *lp; + + lp = list; + if (!lp) + return; + INTOFF; + do { + setvareq(lp->text, flags); + } while ((lp = lp->next)); + INTON; +} + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +char * +lookupvar(const char *name) +{ + struct var *v; + + if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { +#ifdef WITH_LINENO + if (v == &vlineno && v->text == linenovar) { + fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno); + } +#endif + return strchrnul(v->text, '=') + 1; + } + return NULL; +} + +intmax_t lookupvarint(const char *name) +{ + return atomax(lookupvar(name) ?: nullstr, 0); +} + + + +/* + * Generate a list of variables satisfying the given conditions. + */ + +char ** +listvars(int on, int off, char ***end) +{ + struct var **vpp; + struct var *vp; + char **ep; + int mask; + + STARTSTACKSTR(ep); + vpp = vartab; + mask = on | off; + do { + for (vp = *vpp ; vp ; vp = vp->next) + if ((vp->flags & mask) == on) { + if (ep == stackstrend()) + ep = growstackstr(); + *ep++ = (char *) vp->text; + } + } while (++vpp < vartab + VTABSIZE); + if (ep == stackstrend()) + ep = growstackstr(); + if (end) + *end = ep; + *ep++ = NULL; + return grabstackstr(ep); +} + + + +/* + * POSIX requires that 'set' (but not export or readonly) output the + * variables in lexicographic order - by the locale's collating order (sigh). + * Maybe we could keep them in an ordered balanced binary tree + * instead of hashed lists. + * For now just roll 'em through qsort for printing... + */ + +int +showvars(const char *prefix, int on, int off) +{ + const char *sep; + char **ep, **epend; + + ep = listvars(on, off, &epend); + qsort(ep, epend - ep, sizeof(char *), vpcmp); + + sep = *prefix ? spcstr : prefix; + + for (; ep < epend; ep++) { + const char *p; + const char *q; + + p = strchrnul(*ep, '='); + q = nullstr; + if (*p) + q = single_quote(++p); + + out1fmt("%s%s%.*s%s\n", prefix, sep, (int)(p - *ep), *ep, q); + } + + return 0; +} + + + +/* + * The export and readonly commands. + */ + +int +exportcmd(int argc, char **argv) +{ + struct var *vp; + char *name; + const char *p; + char **aptr; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + int notp; + + notp = nextopt("p") - 'p'; + if (notp && ((name = *(aptr = argptr)))) { + do { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + if ((vp = *findvar(hashvar(name), name))) { + vp->flags |= flag; + continue; + } + } + setvar(name, p, flag); + } while ((name = *++aptr) != NULL); + } else { + showvars(argv[0], flag, 0); + } + return 0; +} + + +/* + * The "local" command. + */ + +int +localcmd(int argc, char **argv) +{ + char *name; + + if (!localvar_stack) + sh_error("not in a function"); + + argv = argptr; + while ((name = *argv++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +void mklocal(char *name) +{ + struct localvar *lvp; + struct var **vpp; + struct var *vp; + + INTOFF; + lvp = ckmalloc(sizeof (struct localvar)); + if (name[0] == '-' && name[1] == '\0') { + char *p; + p = ckmalloc(sizeof(optlist)); + lvp->text = memcpy(p, optlist, sizeof(optlist)); + vp = NULL; + } else { + char *eq; + + vpp = hashvar(name); + vp = *findvar(vpp, name); + eq = strchr(name, '='); + if (vp == NULL) { + if (eq) + vp = setvareq(name, VSTRFIXED); + else + vp = setvar(name, NULL, VSTRFIXED); + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (eq) + setvareq(name, 0); + } + } + lvp->vp = vp; + lvp->next = localvar_stack->lv; + localvar_stack->lv = lvp; + INTON; +} + + +/* + * Called after a function returns. + * Interrupts must be off. + */ + +void +poplocalvars(int keep) +{ + struct localvar_list *ll; + struct localvar *lvp, *next; + struct var *vp; + + INTOFF; + ll = localvar_stack; + localvar_stack = ll->next; + + next = ll->lv; + ckfree(ll); + + while ((lvp = next) != NULL) { + next = lvp->next; + vp = lvp->vp; + TRACE(("poplocalvar %s", vp ? vp->text : "-")); + if (keep) { + int bits = VSTRFIXED; + + if (lvp->flags != VUNSET) { + if (vp->text == lvp->text) + bits |= VTEXTFIXED; + else if (!(lvp->flags & (VTEXTFIXED|VSTACK))) + ckfree(lvp->text); + } + + vp->flags &= ~bits; + vp->flags |= (lvp->flags & bits); + + if ((vp->flags & + (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET) + unsetvar(vp->text); + } else if (vp == NULL) { /* $- saved */ + memcpy(optlist, lvp->text, sizeof(optlist)); + ckfree(lvp->text); + optschanged(); + } else if (lvp->flags == VUNSET) { + vp->flags &= ~(VSTRFIXED|VREADONLY); + unsetvar(vp->text); + } else { + if (vp->func) + (*vp->func)(strchrnul(lvp->text, '=') + 1); + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } + INTON; +} + + +/* + * Create a new localvar environment. + */ +struct localvar_list *pushlocalvars(void) +{ + struct localvar_list *ll; + + INTOFF; + ll = ckmalloc(sizeof(*ll)); + ll->lv = NULL; + ll->next = localvar_stack; + localvar_stack = ll; + INTON; + + return ll->next; +} + + +void unwindlocalvars(struct localvar_list *stop) +{ + while (localvar_stack != stop) + poplocalvars(0); +} + + +/* + * The unset builtin command. We unset the function before we unset the + * variable to allow a function to be unset when there is a readonly variable + * with the same name. + */ + +int +unsetcmd(int argc, char **argv) +{ + char **ap; + int i; + int flag = 0; + + while ((i = nextopt("vf")) != '\0') { + flag = i; + } + + for (ap = argptr; *ap ; ap++) { + if (flag != 'f') { + unsetvar(*ap); + continue; + } + if (flag != 'v') + unsetfunc(*ap); + } + return 0; +} + + +/* + * Unset the specified variable. + */ + +void unsetvar(const char *s) +{ + setvar(s, 0, 0); +} + + + +/* + * Find the appropriate entry in the hash table from the name. + */ + +STATIC struct var ** +hashvar(const char *p) +{ + unsigned int hashval; + + hashval = ((unsigned char) *p) << 4; + while (*p && *p != '=') + hashval += (unsigned char) *p++; + return &vartab[hashval % VTABSIZE]; +} + + + +/* + * Compares two strings up to the first = or '\0'. The first + * string must be terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +int +varcmp(const char *p, const char *q) +{ + int c, d; + + while ((c = *p) == (d = *q)) { + if (!c || c == '=') + goto out; + p++; + q++; + } + if (c == '=') + c = 0; + if (d == '=') + d = 0; +out: + return c - d; +} + +STATIC int +vpcmp(const void *a, const void *b) +{ + return varcmp(*(const char **)a, *(const char **)b); +} + +STATIC struct var ** +findvar(struct var **vpp, const char *name) +{ + for (; *vpp; vpp = &(*vpp)->next) { + if (varequal((*vpp)->text, name)) { + break; + } + } + return vpp; +} diff --git a/usr/dash/var.h b/usr/dash/var.h new file mode 100644 index 0000000..1a06a3c --- /dev/null +++ b/usr/dash/var.h @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * @(#)var.h 8.2 (Berkeley) 5/4/95 + */ + +#include <inttypes.h> + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is statically allocated */ +#define VTEXTFIXED 0x08 /* text is statically allocated */ +#define VSTACK 0x10 /* text is allocated on the stack */ +#define VUNSET 0x20 /* the variable is not set */ +#define VNOFUNC 0x40 /* don't call the callback function */ +#define VNOSET 0x80 /* do not set variable - just readonly test */ +#define VNOSAVE 0x100 /* when text is on the heap before setvareq */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + const char *text; /* name=value */ + void (*func)(const char *); + /* function to be called when */ + /* the variable gets set/unset */ +}; + + +struct localvar { + struct localvar *next; /* next local variable in list */ + struct var *vp; /* the variable that was made local */ + int flags; /* saved flags */ + const char *text; /* saved text */ +}; + +struct localvar_list; + + +extern struct localvar *localvars; +extern struct var varinit[]; + +#if ATTY +#define vatty varinit[0] +#define vifs varinit[1] +#else +#define vifs varinit[0] +#endif +#define vmail (&vifs)[1] +#define vmpath (&vmail)[1] +#define vpath (&vmpath)[1] +#define vps1 (&vpath)[1] +#define vps2 (&vps1)[1] +#define vps4 (&vps2)[1] +#define voptind (&vps4)[1] +#ifdef WITH_LINENO +#define vlineno (&voptind)[1] +#endif +#ifndef SMALL +#ifdef WITH_LINENO +#define vterm (&vlineno)[1] +#else +#define vterm (&voptind)[1] +#endif +#define vhistsize (&vterm)[1] +#endif + +#ifdef IFS_BROKEN +extern const char defifsvar[]; +#define defifs (defifsvar + 4) +#else +extern const char defifs[]; +#endif +extern const char defpathvar[]; +#define defpath (defpathvar + 5) + +extern int lineno; +extern char linenovar[]; + +/* + * The following macros access the values of the above variables. + * They have to skip over the name. They return the null string + * for unset variables. + */ + +#define ifsval() (vifs.text + 4) +#define ifsset() ((vifs.flags & VUNSET) == 0) +#define mailval() (vmail.text + 5) +#define mpathval() (vmpath.text + 9) +#define pathval() (vpath.text + 5) +#define ps1val() (vps1.text + 4) +#define ps2val() (vps2.text + 4) +#define ps4val() (vps4.text + 4) +#define optindval() (voptind.text + 7) +#define linenoval() (vlineno.text + 7) +#ifndef SMALL +#define histsizeval() (vhistsize.text + 9) +#define termval() (vterm.text + 5) +#endif + +#if ATTY +#define attyset() ((vatty.flags & VUNSET) == 0) +#endif +#define mpathset() ((vmpath.flags & VUNSET) == 0) + +void initvar(void); +struct var *setvar(const char *name, const char *val, int flags); +intmax_t setvarint(const char *, intmax_t, int); +struct var *setvareq(char *s, int flags); +struct strlist; +void listsetvar(struct strlist *, int); +char *lookupvar(const char *); +intmax_t lookupvarint(const char *); +char **listvars(int, int, char ***); +#define environment() listvars(VEXPORT, VUNSET, 0) +int showvars(const char *, int, int); +int exportcmd(int, char **); +int localcmd(int, char **); +void mklocal(char *); +struct localvar_list *pushlocalvars(void); +void poplocalvars(int); +void unwindlocalvars(struct localvar_list *stop); +int unsetcmd(int, char **); +void unsetvar(const char *); +int varcmp(const char *, const char *); + +static inline int varequal(const char *a, const char *b) { + return !varcmp(a, b); +} + +/* + * Search the environment of a builtin command. + */ + +static inline char *bltinlookup(const char *name) +{ + return lookupvar(name); +} diff --git a/usr/gzip/.gitignore b/usr/gzip/.gitignore new file mode 100644 index 0000000..b665e3f --- /dev/null +++ b/usr/gzip/.gitignore @@ -0,0 +1,3 @@ +gunzip +gzip +zcat diff --git a/usr/gzip/COPYING b/usr/gzip/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/usr/gzip/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/usr/gzip/Kbuild b/usr/gzip/Kbuild new file mode 100644 index 0000000..52c57d0 --- /dev/null +++ b/usr/gzip/Kbuild @@ -0,0 +1,26 @@ +# +# Kbuild file for gzip +# + +# The gzip executable +static-y := gzip +gzip-y := gzip.o util.o unzip.o inflate.o + +# Additional targets +always := gunzip zcat + +# Optional ZIP support +gzip-$(CONFIG_KLIBC_ZIP) += zip.o deflate.o trees.o bits.o +cflags-$(CONFIG_KLIBC_ZIP) += -DSUPPORT_ZIP +EXTRA_KLIBCCFLAGS := $(cflags-y) + +# Additionally linked targets +$(obj)/gunzip $(obj)/zcat: $(obj)/gzip + $(call cmd,ln) + +# Cleaning +targets := gzip gzip.g gunzip zcat + +# Targets to install +install-y := gzip +install-link-y := gunzip=gzip zcat=gzip diff --git a/usr/gzip/README b/usr/gzip/README new file mode 100644 index 0000000..fdd7311 --- /dev/null +++ b/usr/gzip/README @@ -0,0 +1,144 @@ +This is the file README for the gzip distribution, version 1.2.4. + +gzip (GNU zip) is a compression utility designed to be a replacement +for 'compress'. Its main advantages over compress are much better +compression and freedom from patented algorithms. The GNU Project +uses it as the standard compression program for its system. + +gzip currently uses by default the LZ77 algorithm used in zip 1.9 (the +portable pkzip compatible archiver). The gzip format was however +designed to accommodate several compression algorithms. See below +for a comparison of zip and gzip. + +gunzip can currently decompress files created by gzip, compress or +pack. The detection of the input format is automatic. For the +gzip format, gunzip checks a 32 bit CRC. For pack, gunzip checks the +uncompressed length. The 'compress' format was not designed to allow +consistency checks. However gunzip is sometimes able to detect a bad +.Z file because there is some redundancy in the .Z compression format. +If you get an error when uncompressing a .Z file, do not assume that +the .Z file is correct simply because the standard uncompress does not +complain. This generally means that the standard uncompress does not +check its input, and happily generates garbage output. + +gzip produces files with a .gz extension. Previous versions of gzip +used the .z extension, which was already used by the 'pack' +Huffman encoder. gunzip is able to decompress .z files (packed +or gzip'ed). + +Several planned features are not yet supported (see the file TODO). +See the file NEWS for a summary of changes since 0.5. See the file +INSTALL for installation instructions. Some answers to frequently +asked questions are given in the file INSTALL, please read it. (In +particular, please don't ask me once more for an /etc/magic entry.) + +WARNING: on several systems, compiler bugs cause gzip to fail, in +particular when optimization options are on. See the section "Special +targets" at the end of the INSTALL file for a list of known problems. +For all machines, use "make check" to check that gzip was compiled +correctly. Try compiling gzip without any optimization if you have a +problem. + +Please send all comments and bug reports by electronic mail to: + Jean-loup Gailly <jloup@chorus.fr> + +or, if this fails, to bug-gnu-utils@prep.ai.mit.edu. +Bug reports should ideally include: + + * The complete output of "gzip -V" (or the contents of revision.h + if you can't get gzip to compile) + * The hardware and operating system (try "uname -a") + * The compiler used to compile (if it is gcc, use "gcc -v") + * A description of the bug behavior + * The input to gzip, that triggered the bug + +If you send me patches for machines I don't have access to, please test them +very carefully. gzip is used for backups, it must be extremely reliable. + +The package crypt++.el is highly recommended to manipulate gzip'ed +file from emacs. It recognizes automatically encrypted and compressed +files when they are first visited or written. It is available via +anonymous ftp to roebling.poly.edu [128.238.5.31] in /pub/crypt++.el. +The same directory contains also patches to dired, ange-ftp and info. +GNU tar 1.11.2 has a -z option to invoke directly gzip, so you don't have to +patch it. The package ftp.uu.net:/languages/emacs-lisp/misc/jka-compr19.el.Z +also supports gzip'ed files. + +The znew and gzexe shell scripts provided with gzip benefit from +(but do not require) the cpmod utility to transfer file attributes. +It is available by anonymous ftp on gatekeeper.dec.com in +/.0/usenet/comp.sources.unix/volume11/cpmod.Z. + +The sample programs zread.c, sub.c and add.c in subdirectory sample +are provided as examples of useful complements to gzip. Read the +comments inside each source file. The perl script ztouch is also +provided as example (not installed by default since it relies on perl). + + +gzip is free software, you can redistribute it and/or modify it under +the terms of the GNU General Public License, a copy of which is +provided under the name COPYING. The latest version of gzip are always +available by ftp in prep.ai.mit.edu:/pub/gnu, or in any of the prep +mirror sites: + +- sources in gzip-*.tar (or .shar or .tar.gz). +- Solaris 2 executables in sparc-sun-solaris2/gzip-binaries-*.tar +- MSDOS lha self-extracting exe in gzip-msdos-*.exe. Once extracted, + copy gzip.exe to gunzip.exe and zcat.exe, or use "gzip -d" to decompress. + gzip386.exe runs much faster but only on 386 and above; it is compiled with + djgpp 1.10 available in directory omnigate.clarkson.edu:/pub/msdos/djgpp. + +A VMS executable is available in ftp.spc.edu:[.macro32.savesets]gzip-1-*.zip +(use [.macro32]unzip.exe to extract). A PRIMOS executable is available +in ftp.lysator.liu.se:/pub/primos/run/gzip.run. +OS/2 executables (16 and 32 bits versions) are available in +ftp.tu-muenchen.de:/pub/comp/os/os2/archiver/gz*-[16,32].zip + +Some ftp servers can automatically make a tar.Z from a tar file. If +you are getting gzip for the first time, you can ask for a tar.Z file +instead of the much larger tar file. + +Many thanks to those who provided me with bug reports and feedback. +See the files THANKS and ChangeLog for more details. + + + Note about zip vs. gzip: + +The name 'gzip' was a very unfortunate choice, because zip and gzip +are two really different programs, although the actual compression and +decompression sources were written by the same persons. A different +name should have been used for gzip, but it is too late to change now. + +zip is an archiver: it compresses several files into a single archive +file. gzip is a simple compressor: each file is compressed separately. +Both share the same compression and decompression code for the +'deflate' method. unzip can also decompress old zip archives +(implode, shrink and reduce methods). gunzip can also decompress files +created by compress and pack. zip 1.9 and gzip do not support +compression methods other than deflation. (zip 1.0 supports shrink and +implode). Better compression methods may be added in future versions +of gzip. zip will always stick to absolute compatibility with pkzip, +it is thus constrained by PKWare, which is a commercial company. The +gzip header format is deliberately different from that of pkzip to +avoid such a constraint. + +On Unix, gzip is mostly useful in combination with tar. GNU tar +1.11.2 has a -z option to invoke gzip automatically. "tar -z" +compresses better than zip, since gzip can then take advantage of +redundancy between distinct files. The drawback is that you must +scan the whole tar.gz file in order to extract a single file near +the end; unzip can directly seek to the end of the zip file. There +is no overhead when you extract the whole archive anyway. +If a member of a .zip archive is damaged, other files can still +be recovered. If a .tar.gz file is damaged, files beyond the failure +point cannot be recovered. (Future versions of gzip will have +error recovery features.) + +gzip and gunzip are distributed as a single program. zip and unzip +are, for historical reasons, two separate programs, although the +authors of these two programs work closely together in the info-zip +team. zip and unzip are not associated with the GNU project. +The sources are available by ftp in + + oak.oakland.edu:/pub/misc/unix/zip19p1.zip + oak.oakland.edu:/pub/misc/unix/unz50p1.tar-z diff --git a/usr/gzip/bits.c b/usr/gzip/bits.c new file mode 100644 index 0000000..4707a08 --- /dev/null +++ b/usr/gzip/bits.c @@ -0,0 +1,200 @@ +/* bits.c -- output variable-length bit strings + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + + +/* + * PURPOSE + * + * Output variable-length bit strings. Compression can be done + * to a file or to memory. (The latter is not supported in this version.) + * + * DISCUSSION + * + * The PKZIP "deflate" file format interprets compressed file data + * as a sequence of bits. Multi-bit strings in the file may cross + * byte boundaries without restriction. + * + * The first bit of each byte is the low-order bit. + * + * The routines in this file allow a variable-length bit value to + * be output right-to-left (useful for literal values). For + * left-to-right output (useful for code strings from the tree routines), + * the bits must have been reversed first with bi_reverse(). + * + * For in-memory compression, the compressed bit stream goes directly + * into the requested output buffer. The input data is read in blocks + * by the mem_read() function. The buffer is limited to 64K on 16 bit + * machines. + * + * INTERFACE + * + * void bi_init (FILE *zipfile) + * Initialize the bit string routines. + * + * void send_bits (int value, int length) + * Write out a bit string, taking the source bits right to + * left. + * + * int bi_reverse (int value, int length) + * Reverse the bits of a bit string, taking the source bits left to + * right and emitting them right to left. + * + * void bi_windup (void) + * Write out any remaining bits in an incomplete byte. + * + * void copy_block(char *buf, unsigned len, int header) + * Copy a stored block to the zip file, storing first the length and + * its one's complement if requested. + * + */ + +#include "tailor.h" +#include "gzip.h" + +#ifdef DEBUG +# include <stdio.h> +#endif + +#ifdef RCSID +static char rcsid[] = "$Id: bits.c,v 1.1 2002/08/18 00:59:21 hpa Exp $"; +#endif + +/* =========================================================================== + * Local data used by the "bit string" routines. + */ + +local file_t zfile; /* output gzip file */ + +local unsigned short bi_buf; +/* Output buffer. bits are inserted starting at the bottom (least significant + * bits). + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +local int bi_valid; +/* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +int (*read_buf) OF((char *buf, unsigned size)); +/* Current input function. Set to mem_read for in-memory compression */ + +#ifdef DEBUG + ulg bits_sent; /* bit length of the compressed data */ +#endif + +/* =========================================================================== + * Initialize the bit string routines. + */ +void bi_init (zipfile) + file_t zipfile; /* output zip file, NO_FILE for in-memory compression */ +{ + zfile = zipfile; + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = 0L; +#endif + + /* Set the defaults for file compression. They are set by memcompress + * for in-memory compression. + */ + if (zfile != NO_FILE) { + read_buf = file_read; + } +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +void send_bits(value, length) + int value; /* value to send */ + int length; /* number of bits */ +{ +#ifdef DEBUG + Tracev((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + bits_sent += (ulg)length; +#endif + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (bi_valid > (int)Buf_size - length) { + bi_buf |= (value << bi_valid); + put_short(bi_buf); + bi_buf = (ush)value >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } else { + bi_buf |= value << bi_valid; + bi_valid += length; + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +void bi_windup() +{ + if (bi_valid > 8) { + put_short(bi_buf); + } else if (bi_valid > 0) { + put_byte(bi_buf); + } + bi_buf = 0; + bi_valid = 0; +#ifdef DEBUG + bits_sent = (bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +void copy_block(buf, len, header) + char *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(); /* align on byte boundary */ + + if (header) { + put_short((ush)len); + put_short((ush)~len); +#ifdef DEBUG + bits_sent += 2*16; +#endif + } +#ifdef DEBUG + bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(*buf++); + } +} diff --git a/usr/gzip/deflate.c b/usr/gzip/deflate.c new file mode 100644 index 0000000..1db44fd --- /dev/null +++ b/usr/gzip/deflate.c @@ -0,0 +1,759 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* + * PURPOSE + * + * Identify new text as repetitions of old text within a fixed- + * length sliding window trailing behind the new text. + * + * DISCUSSION + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many info-zippers for bug reports and testing. + * + * REFERENCES + * + * APPNOTE.TXT documentation file in PKZIP 1.93a distribution. + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + * INTERFACE + * + * void lm_init (int pack_level, ush *flags) + * Initialize the "longest match" routines for a new file + * + * ulg deflate (void) + * Processes a new input file and return its compressed length. Sets + * the compressed length, crc, deflate flags and internal file + * attributes. + */ + +#include <stdio.h> + +#include "tailor.h" +#include "gzip.h" + +#ifdef RCSID +static char rcsid[] = "$Id: deflate.c,v 1.1 2002/08/18 00:59:21 hpa Exp $"; +#endif + +/* =========================================================================== + * Configuration parameters + */ + +/* Compile with MEDIUM_MEM to reduce the memory requirements or + * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the + * entire input file can be held in memory (not possible on 16 bit systems). + * Warning: defining these symbols affects HASH_BITS (see below) and thus + * affects the compression ratio. The compressed output + * is still correct, and might even be smaller in some cases. + */ + +#ifdef SMALL_MEM +# define HASH_BITS 13 /* Number of bits used to hash strings */ +#endif +#ifdef MEDIUM_MEM +# define HASH_BITS 14 +#endif +#ifndef HASH_BITS +# define HASH_BITS 15 + /* For portability to 16 bit machines, do not use values above 15. */ +#endif + +/* To save space (see unlzw.c), we overlay prev+head with tab_prefix and + * window with tab_suffix. Check that we can do this: + */ +#if (WSIZE<<1) > (1<<BITS) + error: cannot overlay window with tab_suffix and prev with tab_prefix0 +#endif +#if HASH_BITS > BITS-1 + error: cannot overlay head with tab_prefix1 +#endif + +#define HASH_SIZE (unsigned)(1<<HASH_BITS) +#define HASH_MASK (HASH_SIZE-1) +#define WMASK (WSIZE-1) +/* HASH_SIZE and WSIZE must be powers of two */ + +#define NIL 0 +/* Tail of hash chains */ + +#define FAST 4 +#define SLOW 2 +/* speed options for the general purpose bit flag */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* =========================================================================== + * Local data used by the "longest match" routines. + */ + +typedef ush Pos; +typedef unsigned IPos; +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +/* DECLARE(uch, window, 2L*WSIZE); */ +/* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least WSIZE + * bytes. With this organization, matches are limited to a distance of + * WSIZE-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: limit the window size to WSIZE+BSZ if SMALL_MEM (the code would + * be less efficient). + */ + +/* DECLARE(Pos, prev, WSIZE); */ +/* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + +/* DECLARE(Pos, head, 1<<HASH_BITS); */ +/* Heads of the hash chains or NIL. */ + +ulg window_size = (ulg)2*WSIZE; +/* window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the + * input file length plus MIN_LOOKAHEAD. + */ + +long block_start; +/* window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + +local unsigned ins_h; /* hash index of string to be inserted */ + +#define H_SHIFT ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH) +/* Number of bits by which ins_h and del_h must be shifted at each + * input step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * H_SHIFT * MIN_MATCH >= HASH_BITS + */ + +unsigned int prev_length; +/* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + unsigned strstart; /* start of string to insert */ + unsigned match_start; /* start of matching string */ +local int eofile; /* flag set at end of input file */ +local unsigned lookahead; /* number of valid bytes ahead in window */ + +unsigned max_chain_length; +/* To speed up deflation, hash chains are never searched beyond this length. + * A higher limit improves compression ratio but degrades the speed. + */ + +local unsigned int max_lazy_match; +/* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +#define max_insert_length max_lazy_match +/* Insert new strings in the hash table only if the match length + * is not greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + +local int compr_level; +/* compression level (1..9) */ + +unsigned good_match; +/* Use a faster search when the previous match is longer than this */ + + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + +typedef struct config { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; +} config; + +#ifdef FULL_SEARCH +# define nice_match MAX_MATCH +#else + int nice_match; /* Stop searching when current match exceeds this */ +#endif + +local config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0}, /* store only */ +/* 1 */ {4, 4, 8, 4}, /* maximum speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8}, +/* 3 */ {4, 6, 32, 32}, + +/* 4 */ {4, 4, 16, 16}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32}, +/* 6 */ {8, 16, 128, 128}, +/* 7 */ {8, 32, 128, 256}, +/* 8 */ {32, 128, 258, 1024}, +/* 9 */ {32, 258, 258, 4096}}; /* maximum compression */ + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +/* =========================================================================== + * Prototypes for local functions. + */ +local void fill_window OF((void)); +local ulg deflate_fast OF((void)); + + int longest_match OF((IPos cur_match)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ +#endif + +#ifdef DEBUG +local void check_match OF((IPos start, IPos match, int length)); +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK) + +/* =========================================================================== + * Insert string s in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of s are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#define INSERT_STRING(s, match_head) \ + (UPDATE_HASH(ins_h, window[(s) + MIN_MATCH-1]), \ + prev[(s) & WMASK] = match_head = head[ins_h], \ + head[ins_h] = (s)) + +/* =========================================================================== + * Initialize the "longest match" routines for a new file + */ +void lm_init (pack_level, flags) + int pack_level; /* 0: store, 1: best speed, 9: best compression */ + ush *flags; /* general purpose bit flag */ +{ + register unsigned j; + + if (pack_level < 1 || pack_level > 9) error("bad pack level"); + compr_level = pack_level; + + /* Initialize the hash table. */ + memzero((char*)head, HASH_SIZE*sizeof(*head)); + + /* prev will be initialized on the fly */ + + /* Set the default configuration parameters: + */ + max_lazy_match = configuration_table[pack_level].max_lazy; + good_match = configuration_table[pack_level].good_length; +#ifndef FULL_SEARCH + nice_match = configuration_table[pack_level].nice_length; +#endif + max_chain_length = configuration_table[pack_level].max_chain; + if (pack_level == 1) { + *flags |= FAST; + } else if (pack_level == 9) { + *flags |= SLOW; + } + /* ??? reduce max_chain_length for binary files */ + + strstart = 0; + block_start = 0L; +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif + + lookahead = read_buf((char*)window, + sizeof(int) <= 2 ? (unsigned)WSIZE : 2*WSIZE); + + if (lookahead == 0 || lookahead == (unsigned)EOF) { + eofile = 1, lookahead = 0; + return; + } + eofile = 0; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + while (lookahead < MIN_LOOKAHEAD && !eofile) fill_window(); + + ins_h = 0; + for (j=0; j<MIN_MATCH-1; j++) UPDATE_HASH(ins_h, window[j]); + /* If lookahead < MIN_MATCH, ins_h is garbage, but this is + * not important since only literal bytes will be emitted. + */ +} + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + */ +#ifndef ASMV +/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or + * match.s. The code is functionally equivalent, so you can use the C version + * if desired. + */ +int longest_match(cur_match) + IPos cur_match; /* current match */ +{ + unsigned chain_length = max_chain_length; /* max hash chain length */ + register uch *scan = window + strstart; /* current string */ + register uch *match; /* matched string */ + register int len; /* length of current match */ + int best_len = prev_length; /* best match length so far */ + IPos limit = strstart > (IPos)MAX_DIST ? strstart - (IPos)MAX_DIST : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + +/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ +#if HASH_BITS < 8 || MAX_MATCH != 258 + error: Code too clever +#endif + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register uch *strend = window + strstart + MAX_MATCH - 1; + register ush scan_start = *(ush*)scan; + register ush scan_end = *(ush*)(scan+best_len-1); +#else + register uch *strend = window + strstart + MAX_MATCH; + register uch scan_end1 = scan[best_len-1]; + register uch scan_end = scan[best_len]; +#endif + + /* Do not waste too much time if we already have a good match: */ + if (prev_length >= good_match) { + chain_length >>= 2; + } + Assert(strstart <= window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(cur_match < strstart, "no future"); + match = window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ush*)(match+best_len-1) != scan_end || + *(ush*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + scan++, match++; + do { + } while (*(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + *(ush*)(scan+=2) == *(ush*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= window+(unsigned)(window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ush*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} +#endif /* ASMV */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(start, match, length) + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (memcmp((char*)window + match, + (char*)window + start, length) != EQUAL) { + fprintf(stderr, + " start %d, match %d, length %d\n", + start, match, length); + error("invalid match"); + } + if (verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(start, match, length) +#endif + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: at least one byte has been read, or eofile is set; + * file reads are performed for at least two bytes (required for the + * translate_eol option). + */ +local void fill_window() +{ + register unsigned n, m; + unsigned more = (unsigned)(window_size - (ulg)lookahead - (ulg)strstart); + /* Amount of free space at the end of the window. */ + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned)EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + } else if (strstart >= WSIZE+MAX_DIST) { + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + Assert(window_size == (ulg)2*WSIZE, "no sliding with BIG_MEM"); + + memcpy((char*)window, (char*)window+WSIZE, (unsigned)WSIZE); + match_start -= WSIZE; + strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + block_start -= (long) WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = head[n]; + head[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = prev[n]; + prev[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + /* At this point, more >= 2 */ + if (!eofile) { + n = read_buf((char*)window+strstart+lookahead, more); + if (n == 0 || n == (unsigned)EOF) { + eofile = 1; + } else { + lookahead += n; + } + } +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(eof) \ + flush_block(block_start >= 0L ? (char*)&window[(unsigned)block_start] : \ + (char*)NULL, (long)strstart - block_start, (eof)) + +/* =========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluationof matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local ulg deflate_fast() +{ + IPos hash_head; /* head of the hash chain */ + int flush; /* set if current block must be flushed */ + unsigned match_length = 0; /* length of best match */ + + prev_length = MIN_MATCH-1; + while (lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length = longest_match (hash_head); + /* longest_match() sets match_start */ + if (match_length > lookahead) match_length = lookahead; + } + if (match_length >= MIN_MATCH) { + check_match(strstart, match_start, match_length); + + flush = ct_tally(strstart-match_start, match_length - MIN_MATCH); + + lookahead -= match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (match_length <= max_insert_length) { + match_length--; /* string at strstart already in hash table */ + do { + strstart++; + INSERT_STRING(strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since + * the next lookahead bytes will be emitted as literals. + */ + } while (--match_length != 0); + strstart++; + } else { + strstart += match_length; + match_length = 0; + ins_h = window[strstart]; + UPDATE_HASH(ins_h, window[strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c",window[strstart])); + flush = ct_tally (0, window[strstart]); + lookahead--; + strstart++; + } + if (flush) FLUSH_BLOCK(0), block_start = strstart; + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (lookahead < MIN_LOOKAHEAD && !eofile) fill_window(); + + } + return FLUSH_BLOCK(1); /* eof */ +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +ulg deflate() +{ + IPos hash_head; /* head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH-1; /* length of best match */ +#ifdef DEBUG + extern long isize; /* byte length of input file, for debug only */ +#endif + + if (compr_level <= 3) return deflate_fast(); /* optimized for speed */ + + /* Process the input block. */ + while (lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + INSERT_STRING(strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + prev_length = match_length, prev_match = match_start; + match_length = MIN_MATCH-1; + + if (hash_head != NIL && prev_length < max_lazy_match && + strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + match_length = longest_match (hash_head); + /* longest_match() sets match_start */ + if (match_length > lookahead) match_length = lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && strstart-match_start > TOO_FAR){ + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length--; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (prev_length >= MIN_MATCH && match_length <= prev_length) { + + check_match(strstart-1, prev_match, prev_length); + + flush = ct_tally(strstart-1-prev_match, prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + lookahead -= prev_length-1; + prev_length -= 2; + do { + strstart++; + INSERT_STRING(strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH + * these bytes are garbage, but it does not matter since the + * next lookahead bytes will always be emitted as literals. + */ + } while (--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH-1; + strstart++; + if (flush) FLUSH_BLOCK(0), block_start = strstart; + + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c",window[strstart-1])); + if (ct_tally (0, window[strstart-1])) { + FLUSH_BLOCK(0), block_start = strstart; + } + strstart++; + lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + strstart++; + lookahead--; + } + Assert (strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + while (lookahead < MIN_LOOKAHEAD && !eofile) fill_window(); + } + if (match_available) ct_tally (0, window[strstart-1]); + + return FLUSH_BLOCK(1); /* eof */ +} diff --git a/usr/gzip/gzip.c b/usr/gzip/gzip.c new file mode 100644 index 0000000..d0c7e00 --- /dev/null +++ b/usr/gzip/gzip.c @@ -0,0 +1,1215 @@ +/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface + * Copyright (C) 1992-1993 Jean-loup Gailly + * The unzip code was written and put in the public domain by Mark Adler. + * Portions of the lzw code are derived from the public domain 'compress' + * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies, + * Ken Turkowski, Dave Mack and Peter Jannesen. + * + * See the license_msg below and the file COPYING for the software license. + * See the file algorithm.doc for the compression algorithms and file formats. + */ + +static char *license_msg[] = { +" Copyright (C) 1992-1993 Jean-loup Gailly", +" This program is free software; you can redistribute it and/or modify", +" it under the terms of the GNU General Public License as published by", +" the Free Software Foundation; either version 2, or (at your option)", +" any later version.", +"", +" This program is distributed in the hope that it will be useful,", +" but WITHOUT ANY WARRANTY; without even the implied warranty of", +" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", +" GNU General Public License for more details.", +"", +" You should have received a copy of the GNU General Public License", +" along with this program; if not, write to the Free Software", +" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.", +0}; + +/* Compress files with zip algorithm and 'compress' interface. + * See usage() and help() functions below for all options. + * Outputs: + * file.gz: compressed file with same mode, owner, and utimes + * or stdout with -c option or if stdin used as input. + * If the output file name had to be truncated, the original name is kept + * in the compressed file. + * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz. + * + * Using gz on MSDOS would create too many file name conflicts. For + * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for + * tar.gz). Similarly, foo.dir and foo.doc would both be mapped to foo.dgz. + * I also considered 12345678.txt -> 12345txt.gz but this truncates the name + * too heavily. There is no ideal solution given the MSDOS 8+3 limitation. + * + * For the meaning of all compilation flags, see comments in Makefile.in. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: gzip.c,v 1.3 2005/02/12 21:03:28 olh Exp $"; +#endif + +#include <ctype.h> +#include <sys/types.h> +#include <signal.h> +#include <sys/stat.h> +#include <errno.h> + +#include "tailor.h" +#include "gzip.h" +#include "revision.h" + +#include <time.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <utime.h> + +typedef void (*sig_type) OF((int)); + +#define RW_USER (S_IRUSR | S_IWUSR) /* creation mode for open() */ + +#ifndef MAX_PATH_LEN +# define MAX_PATH_LEN 1024 /* max pathname length */ +#endif + + /* global buffers */ + +DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA); +DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA); +DECLARE(ush, d_buf, DIST_BUFSIZE); +DECLARE(uch, window, 2L*WSIZE); +DECLARE(ush, tab_prefix, 1L<<BITS); + + /* local variables */ + +#ifndef SUPPORT_ZIP +#define decompress 1 +#else +int level = 6; /* compression level */ +#endif + +int to_stdout; /* output to stdout (-c) */ +#ifdef decompress +int decompress_wanted; +#else +int decompress; /* decompress (-d) */ +#define decompress_wanted decompress +#endif +int force; /* don't ask questions, compress links (-f) */ +int no_name = -1; /* don't save or restore the original file name */ +int no_time = -1; /* don't save or restore the original file time */ +int verbose; /* be verbose (-v) */ +int quiet; /* be very quiet (-q) */ +int test; /* test .gz file integrity */ +int foreground; /* set if program run in foreground */ +char *progname; /* program name */ +int method = DEFLATED;/* compression method */ +int exit_code = OK; /* program exit code */ +int save_orig_name; /* set if original name must be saved */ +int last_member; /* set for .zip and .Z files */ +int part_nb; /* number of parts in .gz file */ +time_t time_stamp; /* original time stamp (modification time) */ +long ifile_size; /* input file size, -1 for devices (debug only) */ +char *env; /* contents of GZIP env variable */ +char **args = NULL; /* argv pointer if GZIP env variable defined */ +char z_suffix[MAX_SUFFIX+1]; /* default suffix (can be set with --suffix) */ +int z_len; /* strlen(z_suffix) */ + +long header_bytes; /* number of bytes in gzip header */ +long bytes_in; /* number of input bytes */ +long bytes_out; /* number of output bytes */ +long total_in; /* input bytes for all files */ +long total_out; /* output bytes for all files */ +char ifname[MAX_PATH_LEN]; /* input file name */ +char ofname[MAX_PATH_LEN]; /* output file name */ +int remove_ofname; /* remove output file on error */ +struct stat istat; /* status for input file */ +int ifd; /* input file descriptor */ +int ofd; /* output file descriptor */ +unsigned insize; /* valid bytes in inbuf */ +unsigned inptr; /* index of next byte to be processed in inbuf */ +unsigned outcnt; /* bytes in output buffer */ + +/* local functions */ + +local void usage OF((void)); +local void help OF((void)); +local void license OF((void)); +local void version OF((void)); +local void treat_stdin OF((void)); +local void treat_file OF((char *iname)); +local int create_outfile OF((void)); +local int do_stat OF((char *name, struct stat *sbuf)); +local char *get_suffix OF((char *name)); +local int get_istat OF((char *iname, struct stat *sbuf)); +local int make_ofname OF((void)); +local int same_file OF((struct stat *stat1, struct stat *stat2)); +local int name_too_long OF((char *name, struct stat *statb)); +local void shorten_name OF((char *name)); +local int get_method OF((void)); +local int check_ofname OF((void)); +local void copy_stat OF((struct stat *ifstat)); +local void do_exit OF((int exitcode)); + int main OF((int argc, char **argv)); +int (*work) OF((int infile, int outfile)) +#ifdef SUPPORT_ZIP + = zip; /* function to call */ +#else + = unzip; +#endif +local void reset_times OF((char *name, struct stat *statb)); + +#define strequ(s1, s2) (strcmp((s1),(s2)) == 0) + +/* ======================================================================== */ +local void usage() +{ + fprintf(stderr, "usage: %s [-cdfhlLnNtvV19] [-S suffix] [file ...]\n", + progname); +} + +/* ======================================================================== */ +local void help() +{ + static char *help_msg[] = { + " -c --stdout write on standard output, keep original files unchanged", + " -d --decompress decompress", + " -f --force force overwrite of output file and compress links", + " -h --help give this help", + " -L --license display software license", +#ifdef UNDOCUMENTED + " -m --no-time do not save or restore the original modification time", + " -M --time save or restore the original modification time", +#endif + " -n --no-name do not save or restore the original name and time stamp", + " -N --name save or restore the original name and time stamp", + " -q --quiet suppress all warnings", + " -S .suf --suffix .suf use suffix .suf on compressed files", + " -t --test test compressed file integrity", + " -v --verbose verbose mode", + " -V --version display version number", +#ifdef SUPPORT_ZIP + " -1 --fast compress faster", + " -9 --best compress better", + " file... files to (de)compress. If none given, use standard input.", +#else + " file... files to decompress. If none given, use standard input.", +#endif + 0}; + char **p = help_msg; + + fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE); + usage(); + while (*p) fprintf(stderr, "%s\n", *p++); +} + +/* ======================================================================== */ +local void license() +{ + char **p = license_msg; + + fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE); + while (*p) fprintf(stderr, "%s\n", *p++); +} + +/* ======================================================================== */ +local void version() +{ + fprintf(stderr,"%s %s (%s)\n", progname, VERSION, REVDATE); + + fprintf(stderr, "Compilation options: UTIME STDC_HEADERS" +#ifndef SUPPORT_ZIP + " DECOMPRESS_ONLY" +#endif + "\n"); +} + +/* ======================================================================== */ +int main (argc, argv) + int argc; + char **argv; +{ + int file_count; /* number of files to precess */ + int optc; /* current option */ + + progname = basename(argv[0]); + + /* Add options in GZIP environment variable if there is one */ + env = add_envopt(&argc, &argv, OPTIONS_VAR); + if (env != NULL) args = argv; + + foreground = sysv_signal(SIGINT, SIG_IGN) != SIG_IGN; + if (foreground) { + (void) sysv_signal(SIGINT, (sig_type)abort_gzip); + } +#ifdef SIGTERM + if (sysv_signal(SIGTERM, SIG_IGN) != SIG_IGN) { + (void) sysv_signal(SIGTERM, (sig_type)abort_gzip); + } +#endif +#ifdef SIGHUP + if (sysv_signal(SIGHUP, SIG_IGN) != SIG_IGN) { + (void) sysv_signal(SIGHUP, (sig_type)abort_gzip); + } +#endif + +#ifndef GNU_STANDARD + /* For compatibility with old compress, use program name as an option. + * If you compile with -DGNU_STANDARD, this program will behave as + * gzip even if it is invoked under the name gunzip or zcat. + * + * Systems which do not support links can still use -d or -dc. + * Ignore an .exe extension for MSDOS, OS/2 and VMS. + */ + if ( strncmp(progname, "un", 2) == 0 /* ungzip, uncompress */ + || strncmp(progname, "gun", 3) == 0) { /* gunzip */ + decompress_wanted = 1; + } + if (strequ(progname+1, "cat") /* zcat, pcat, gcat */ + || strequ(progname, "gzcat")) { /* gzcat */ + decompress_wanted = 1; + to_stdout = 1; + } +#endif + + strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix)-1); + z_len = strlen(z_suffix); + + while ((optc = getopt(argc, argv, "cdfhH?LmMnNqrS:tvV123456789")) != EOF) { + switch (optc) { + case 'c': + to_stdout = 1; break; + case 'd': + decompress_wanted = 1; + break; + case 'f': + force++; break; + case 'h': case 'H': case '?': + help(); do_exit(OK); break; + case 'L': + license(); do_exit(OK); break; + case 'm': /* undocumented, may change later */ + no_time = 1; break; + case 'M': /* undocumented, may change later */ + no_time = 0; break; + case 'n': + no_name = no_time = 1; break; + case 'N': + no_name = no_time = 0; break; + case 'q': + quiet = 1; verbose = 0; break; + case 'S': + z_len = strlen(optarg); + strcpy(z_suffix, optarg); + break; + case 't': + test = to_stdout = 1; + decompress_wanted = 1; + break; + case 'v': + verbose++; quiet = 0; break; + case 'V': + version(); do_exit(OK); break; +#ifdef SUPPORT_ZIP + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + level = optc - '0'; + break; +#endif + default: + /* Error message already emitted by getopt_long. */ + usage(); + do_exit(ERROR); + } + } /* loop on all arguments */ + +#ifndef SUPPORT_ZIP + if (!decompress_wanted) { + fprintf(stderr, "%s: this version does not support compression\n", + progname); + do_exit(ERROR); + } +#endif + + /* By default, save name and timestamp on compression but do not + * restore them on decompression. + */ + if (no_time < 0) no_time = decompress; + if (no_name < 0) no_name = decompress; + + file_count = argc - optind; + + if ((z_len == 0 && !decompress) || z_len > MAX_SUFFIX) { + fprintf(stderr, "%s: incorrect suffix '%s'\n", + progname, optarg); + do_exit(ERROR); + } + + /* Allocate all global buffers (for DYN_ALLOC option) */ + ALLOC(uch, inbuf, INBUFSIZ +INBUF_EXTRA); + ALLOC(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA); + ALLOC(ush, d_buf, DIST_BUFSIZE); + ALLOC(uch, window, 2L*WSIZE); + ALLOC(ush, tab_prefix, 1L<<BITS); + + /* And get to work */ + if (file_count != 0) { + while (optind < argc) { + treat_file(argv[optind++]); + } + } else { /* Standard input */ + treat_stdin(); + } + do_exit(exit_code); + return exit_code; /* just to avoid lint warning */ +} + +/* ======================================================================== + * Compress or decompress stdin + */ +local void treat_stdin() +{ + if (!force && + isatty(fileno((FILE *)(decompress ? stdin : stdout)))) { + /* Do not send compressed data to the terminal or read it from + * the terminal. We get here when user invoked the program + * without parameters, so be helpful. According to the GNU standards: + * + * If there is one behavior you think is most useful when the output + * is to a terminal, and another that you think is most useful when + * the output is a file or a pipe, then it is usually best to make + * the default behavior the one that is useful with output to a + * terminal, and have an option for the other behavior. + * + * Here we use the --force option to get the other behavior. + */ + fprintf(stderr, + "%s: compressed data not %s a terminal. Use -f to force %scompression.\n", + progname, decompress ? "read from" : "written to", + decompress ? "de" : ""); + fprintf(stderr,"For help, type: %s -h\n", progname); + do_exit(ERROR); + } + + strcpy(ifname, "stdin"); + strcpy(ofname, "stdout"); + + /* Get the time stamp on the input file. */ + time_stamp = 0; /* time unknown by default */ + + if (!no_time) { + if (fstat(fileno(stdin), &istat) != 0) { + error("fstat(stdin)"); + } + time_stamp = istat.st_mtime; + } + ifile_size = -1L; /* convention for unknown size */ + + clear_bufs(); /* clear input and output buffers */ + to_stdout = 1; + part_nb = 0; + + if (decompress) { + method = get_method(); + if (method < 0) { + do_exit(exit_code); /* error message already emitted */ + } + } + + /* Actually do the compression/decompression. Loop over zipped members. + */ + for (;;) { + if ((*work)(fileno(stdin), fileno(stdout)) != OK) return; + + if (!decompress || last_member || inptr == insize) break; + /* end of file */ + + method = get_method(); + if (method < 0) return; /* error message already emitted */ + bytes_out = 0; /* required for length check */ + } + + if (verbose) { + if (test) { + fprintf(stderr, " OK\n"); + + } else if (!decompress) { + display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr); + fprintf(stderr, "\n"); +#ifdef DISPLAY_STDIN_RATIO + } else { + display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr); + fprintf(stderr, "\n"); +#endif + } + } +} + +/* ======================================================================== + * Compress or decompress the given file + */ +local void treat_file(iname) + char *iname; +{ + /* Accept "-" as synonym for stdin */ + if (strequ(iname, "-")) { + int cflag = to_stdout; + treat_stdin(); + to_stdout = cflag; + return; + } + + /* Check if the input file is present, set ifname and istat: */ + if (get_istat(iname, &istat) != OK) return; + + /* If the input name is that of a directory, recurse or ignore: */ + if (S_ISDIR(istat.st_mode)) { + WARN((stderr,"%s: %s is a directory -- ignored\n", progname, ifname)); + return; + } + if (!S_ISREG(istat.st_mode)) { + WARN((stderr, + "%s: %s is not a directory or a regular file - ignored\n", + progname, ifname)); + return; + } + if (istat.st_nlink > 1 && !to_stdout && !force) { + WARN((stderr, "%s: %s has %d other link%c -- unchanged\n", + progname, ifname, + (int)istat.st_nlink - 1, istat.st_nlink > 2 ? 's' : ' ')); + return; + } + + ifile_size = istat.st_size; + time_stamp = no_time ? 0 : istat.st_mtime; + + /* Generate output file name. For -r and (-t or -l), skip files + * without a valid gzip suffix (check done in make_ofname). + */ + if (to_stdout && !test) { + strcpy(ofname, "stdout"); + + } else if (make_ofname() != OK) { + return; + } + + /* Open the input file and determine compression method. The mode + * parameter is ignored but required by some systems (VMS) and forbidden + * on other systems (MacOS). + */ + ifd = open(ifname, !decompress ? O_RDONLY : O_RDONLY, + RW_USER); + if (ifd == -1) { + fprintf(stderr, "%s: ", progname); + perror(ifname); + exit_code = ERROR; + return; + } + clear_bufs(); /* clear input and output buffers */ + part_nb = 0; + + if (decompress) { + method = get_method(); /* updates ofname if original given */ + if (method < 0) { + close(ifd); + return; /* error message already emitted */ + } + } + + /* If compressing to a file, check if ofname is not ambiguous + * because the operating system truncates names. Otherwise, generate + * a new ofname and save the original name in the compressed file. + */ + if (to_stdout) { + ofd = fileno(stdout); + /* keep remove_ofname as zero */ + } else { + if (create_outfile() != OK) return; + + if (!decompress && save_orig_name && !verbose && !quiet) { + fprintf(stderr, "%s: %s compressed to %s\n", + progname, ifname, ofname); + } + } + /* Keep the name even if not truncated except with --no-name: */ + if (!save_orig_name) save_orig_name = !no_name; + + if (verbose) { + fprintf(stderr, "%s:\t%s", ifname, (int)strlen(ifname) >= 15 ? + "" : ((int)strlen(ifname) >= 7 ? "\t" : "\t\t")); + } + + /* Actually do the compression/decompression. Loop over zipped members. + */ + for (;;) { + if ((*work)(ifd, ofd) != OK) { + method = -1; /* force cleanup */ + break; + } + if (!decompress || last_member || inptr == insize) break; + /* end of file */ + + method = get_method(); + if (method < 0) break; /* error message already emitted */ + bytes_out = 0; /* required for length check */ + } + + close(ifd); + if (!to_stdout && close(ofd)) { + write_error(); + } + if (method == -1) { + if (!to_stdout) unlink (ofname); + return; + } + /* Display statistics */ + if(verbose) { + if (test) { + fprintf(stderr, " OK"); + } else if (decompress) { + display_ratio(bytes_out-(bytes_in-header_bytes), bytes_out,stderr); + } else { + display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr); + } + if (!test && !to_stdout) { + fprintf(stderr, " -- replaced with %s", ofname); + } + fprintf(stderr, "\n"); + } + /* Copy modes, times, ownership, and remove the input file */ + if (!to_stdout) { + copy_stat(&istat); + } +} + +/* ======================================================================== + * Create the output file. Return OK or ERROR. + * Try several times if necessary to avoid truncating the z_suffix. For + * example, do not create a compressed file of name "1234567890123." + * Sets save_orig_name to true if the file name has been truncated. + * IN assertions: the input file has already been open (ifd is set) and + * ofname has already been updated if there was an original name. + * OUT assertions: ifd and ofd are closed in case of error. + */ +local int create_outfile() +{ + struct stat ostat; /* stat for ofname */ + int flags = O_WRONLY | O_CREAT | O_EXCL; + + for (;;) { + /* Make sure that ofname is not an existing file */ + if (check_ofname() != OK) { + close(ifd); + return ERROR; + } + /* Create the output file */ + remove_ofname = 1; + ofd = open(ofname, flags, RW_USER); + if (ofd == -1) { + perror(ofname); + close(ifd); + exit_code = ERROR; + return ERROR; + } + + /* Check for name truncation on new file (1234567890123.gz) */ + if (fstat(ofd, &ostat) != 0) { + fprintf(stderr, "%s: ", progname); + perror(ofname); + close(ifd); close(ofd); + unlink(ofname); + exit_code = ERROR; + return ERROR; + } + if (!name_too_long(ofname, &ostat)) return OK; + + if (decompress) { + /* name might be too long if an original name was saved */ + WARN((stderr, "%s: %s: warning, name truncated\n", + progname, ofname)); + return OK; + } + close(ofd); + unlink(ofname); + shorten_name(ofname); + } +} + +/* ======================================================================== + * Use lstat if available, except for -c or -f. Use stat otherwise. + * This allows links when not removing the original file. + */ +local int do_stat(name, sbuf) + char *name; + struct stat *sbuf; +{ + errno = 0; + if (!to_stdout && !force) { + return lstat(name, sbuf); + } + return stat(name, sbuf); +} + +/* ======================================================================== + * Return a pointer to the 'z' suffix of a file name, or NULL. For all + * systems, ".gz", ".z", ".Z", ".taz", ".tgz", "-gz", "-z" and "_z" are + * accepted suffixes, in addition to the value of the --suffix option. + * ".tgz" is a useful convention for tar.z files on systems limited + * to 3 characters extensions. On such systems, ".?z" and ".??z" are + * also accepted suffixes. For Unix, we do not want to accept any + * .??z suffix as indicating a compressed file; some people use .xyz + * to denote volume data. + * On systems allowing multiple versions of the same file (such as VMS), + * this function removes any version suffix in the given name. + */ +local char *get_suffix(name) + char *name; +{ + int nlen, slen; + char suffix[MAX_SUFFIX+3]; /* last chars of name, forced to lower case */ + static char *known_suffixes[] = + {z_suffix, ".gz", ".z", ".taz", ".tgz", "-gz", "-z", "_z", + NULL}; + char **suf = known_suffixes; + + if (strequ(z_suffix, "z")) suf++; /* check long suffixes first */ + + nlen = strlen(name); + if (nlen <= MAX_SUFFIX+2) { + strcpy(suffix, name); + } else { + strcpy(suffix, name+nlen-MAX_SUFFIX-2); + } + strlwr(suffix); + slen = strlen(suffix); + do { + int s = strlen(*suf); + if (slen > s && suffix[slen-s-1] != PATH_SEP + && strequ(suffix + slen - s, *suf)) { + return name+nlen-s; + } + } while (*++suf != NULL); + + return NULL; +} + + +/* ======================================================================== + * Set ifname to the input file name (with a suffix appended if necessary) + * and istat to its stats. For decompression, if no file exists with the + * original name, try adding successively z_suffix, .gz, .z, -z and .Z. + * For MSDOS, we try only z_suffix and z. + * Return OK or ERROR. + */ +local int get_istat(iname, sbuf) + char *iname; + struct stat *sbuf; +{ + int ilen; /* strlen(ifname) */ + static char *suffixes[] = {z_suffix, ".gz", ".z", "-z", ".Z", NULL}; + char **suf = suffixes; + char *s; + + strcpy(ifname, iname); + + /* If input file exists, return OK. */ + if (do_stat(ifname, sbuf) == 0) return OK; + + if (!decompress || errno != ENOENT) { + perror(ifname); + exit_code = ERROR; + return ERROR; + } + /* file.ext doesn't exist, try adding a suffix (after removing any + * version number for VMS). + */ + s = get_suffix(ifname); + if (s != NULL) { + perror(ifname); /* ifname already has z suffix and does not exist */ + exit_code = ERROR; + return ERROR; + } + ilen = strlen(ifname); + if (strequ(z_suffix, ".gz")) suf++; + + /* Search for all suffixes */ + do { + s = *suf; + strcat(ifname, s); + if (do_stat(ifname, sbuf) == 0) return OK; + ifname[ilen] = '\0'; + } while (*++suf != NULL); + + /* No suffix found, complain using z_suffix: */ + strcat(ifname, z_suffix); + perror(ifname); + exit_code = ERROR; + return ERROR; +} + +/* ======================================================================== + * Generate ofname given ifname. Return OK, or WARNING if file must be skipped. + * Sets save_orig_name to true if the file name has been truncated. + */ +local int make_ofname() +{ + char *suff; /* ofname z suffix */ + + strcpy(ofname, ifname); + /* strip a version number if any and get the gzip suffix if present: */ + suff = get_suffix(ofname); + + if (decompress) { + if (suff == NULL) { + /* Whith -t or -l, try all files (even without .gz suffix) + * except with -r (behave as with just -dr). + */ + if (test) return OK; + + /* Avoid annoying messages with -r */ + if (verbose || !quiet) { + WARN((stderr,"%s: %s: unknown suffix -- ignored\n", + progname, ifname)); + } + return WARNING; + } + /* Make a special case for .tgz and .taz: */ + strlwr(suff); + if (strequ(suff, ".tgz") || strequ(suff, ".taz")) { + strcpy(suff, ".tar"); + } else { + *suff = '\0'; /* strip the z suffix */ + } + /* ofname might be changed later if infile contains an original name */ + + } else if (suff != NULL) { + /* Avoid annoying messages with -r (see treat_dir()) */ + if (verbose || !quiet) { + fprintf(stderr, "%s: %s already has %s suffix -- unchanged\n", + progname, ifname, suff); + } + if (exit_code == OK) exit_code = WARNING; + return WARNING; + } else { + save_orig_name = 0; + strcat(ofname, z_suffix); + + } /* decompress ? */ + return OK; +} + + +/* ======================================================================== + * Check the magic number of the input file and update ofname if an + * original name was given and to_stdout is not set. + * Return the compression method, -1 for error, -2 for warning. + * Set inptr to the offset of the next byte to be processed. + * Updates time_stamp if there is one and --no-time is not used. + * This function may be called repeatedly for an input file consisting + * of several contiguous gzip'ed members. + * IN assertions: there is at least one remaining compressed member. + * If the member is a zip file, it must be the only one. + */ +local int get_method() +{ + uch flags; /* compression flags */ + char magic[2]; /* magic header */ + ulg stamp; /* time stamp */ + + /* If --force and --stdout, zcat == cat, so do not complain about + * premature end of file: use try_byte instead of get_byte. + */ + if (force && to_stdout) { + magic[0] = (char)try_byte(); + magic[1] = (char)try_byte(); + /* If try_byte returned EOF, magic[1] == 0xff */ + } else { + magic[0] = (char)get_byte(); + magic[1] = (char)get_byte(); + } + method = -1; /* unknown yet */ + part_nb++; /* number of parts in gzip file */ + header_bytes = 0; + last_member = RECORD_IO; + /* assume multiple members in gzip file except for record oriented I/O */ + + if (memcmp(magic, GZIP_MAGIC, 2) == 0 + || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) { + + method = (int)get_byte(); + if (method != DEFLATED) { + fprintf(stderr, + "%s: %s: unknown method %d -- get newer version of gzip\n", + progname, ifname, method); + exit_code = ERROR; + return -1; + } + work = unzip; + flags = (uch)get_byte(); + + if ((flags & ENCRYPTED) != 0) { + fprintf(stderr, + "%s: %s is encrypted -- get newer version of gzip\n", + progname, ifname); + exit_code = ERROR; + return -1; + } + if ((flags & CONTINUATION) != 0) { + fprintf(stderr, + "%s: %s is a a multi-part gzip file -- get newer version of gzip\n", + progname, ifname); + exit_code = ERROR; + if (force <= 1) return -1; + } + if ((flags & RESERVED) != 0) { + fprintf(stderr, + "%s: %s has flags 0x%x -- get newer version of gzip\n", + progname, ifname, flags); + exit_code = ERROR; + if (force <= 1) return -1; + } + stamp = (ulg)get_byte(); + stamp |= ((ulg)get_byte()) << 8; + stamp |= ((ulg)get_byte()) << 16; + stamp |= ((ulg)get_byte()) << 24; + if (stamp != 0 && !no_time) time_stamp = stamp; + + (void)get_byte(); /* Ignore extra flags for the moment */ + (void)get_byte(); /* Ignore OS type for the moment */ + + if ((flags & CONTINUATION) != 0) { + unsigned part = (unsigned)get_byte(); + part |= ((unsigned)get_byte())<<8; + if (verbose) { + fprintf(stderr,"%s: %s: part number %u\n", + progname, ifname, part); + } + } + if ((flags & EXTRA_FIELD) != 0) { + unsigned len = (unsigned)get_byte(); + len |= ((unsigned)get_byte())<<8; + if (verbose) { + fprintf(stderr,"%s: %s: extra field of %u bytes ignored\n", + progname, ifname, len); + } + while (len--) (void)get_byte(); + } + + /* Get original file name if it was truncated */ + if ((flags & ORIG_NAME) != 0) { + if (no_name || to_stdout || part_nb > 1) { + /* Discard the old name */ + char c; /* dummy used for NeXTstep 3.0 cc optimizer bug */ + do {c=get_byte();} while (c != 0); + } else { + /* Copy the base name. Keep a directory prefix intact. */ + char *p = basename(ofname); + for (;;) { + *p = (char)get_char(); + if (*p++ == '\0') break; + if (p >= ofname+sizeof(ofname)) { + error("corrupted input -- file name too large"); + } + } + } /* no_name || to_stdout */ + } /* ORIG_NAME */ + + /* Discard file comment if any */ + if ((flags & COMMENT) != 0) { + while (get_char() != 0) /* null */ ; + } + if (part_nb == 1) { + header_bytes = inptr + 2*sizeof(long); /* include crc and size */ + } + } else if (force && to_stdout) { /* pass input unchanged */ + method = STORED; + work = copy; + inptr = 0; + last_member = 1; + } + if (method >= 0) return method; + + if (part_nb == 1) { + fprintf(stderr, "\n%s: %s: not in gzip format\n", progname, ifname); + exit_code = ERROR; + return -1; + } else { + WARN((stderr, "\n%s: %s: decompression OK, trailing garbage ignored\n", + progname, ifname)); + return -2; + } +} + + +/* ======================================================================== + * Return true if the two stat structures correspond to the same file. + */ +local int same_file(stat1, stat2) + struct stat *stat1; + struct stat *stat2; +{ + return stat1->st_ino == stat2->st_ino + && stat1->st_dev == stat2->st_dev +#ifdef NO_ST_INO + /* Can't rely on st_ino and st_dev, use other fields: */ + && stat1->st_mode == stat2->st_mode + && stat1->st_uid == stat2->st_uid + && stat1->st_gid == stat2->st_gid + && stat1->st_size == stat2->st_size + && stat1->st_atime == stat2->st_atime + && stat1->st_mtime == stat2->st_mtime + && stat1->st_ctime == stat2->st_ctime +#endif + ; +} + +/* ======================================================================== + * Return true if a file name is ambiguous because the operating system + * truncates file names. + */ +local int name_too_long(name, statb) + char *name; /* file name to check */ + struct stat *statb; /* stat buf for this file name */ +{ + int s = strlen(name); + char c = name[s-1]; + struct stat tstat; /* stat for truncated name */ + int res; + + tstat = *statb; /* Just in case OS does not fill all fields */ + name[s-1] = '\0'; + res = stat(name, &tstat) == 0 && same_file(statb, &tstat); + name[s-1] = c; + Trace((stderr, " too_long(%s) => %d\n", name, res)); + return res; +} + +/* ======================================================================== + * Shorten the given name by one character, or replace a .tar extension + * with .tgz. Truncate the last part of the name which is longer than + * MIN_PART characters: 1234.678.012.gz -> 123.678.012.gz. If the name + * has only parts shorter than MIN_PART truncate the longest part. + * For decompression, just remove the last character of the name. + * + * IN assertion: for compression, the suffix of the given name is z_suffix. + */ +local void shorten_name(name) + char *name; +{ + int len; /* length of name without z_suffix */ + char *trunc = NULL; /* character to be truncated */ + int plen; /* current part length */ + int min_part = MIN_PART; /* current minimum part length */ + char *p; + + len = strlen(name); + if (decompress) { + if (len <= 1) error("name too short"); + name[len-1] = '\0'; + return; + } + p = get_suffix(name); + if (p == NULL) error("can't recover suffix\n"); + *p = '\0'; + save_orig_name = 1; + + /* compress 1234567890.tar to 1234567890.tgz */ + if (len > 4 && strequ(p-4, ".tar")) { + strcpy(p-4, ".tgz"); + return; + } + /* Try keeping short extensions intact: + * 1234.678.012.gz -> 123.678.012.gz + */ + do { + p = strrchr(name, PATH_SEP); + p = p ? p+1 : name; + while (*p) { + plen = strcspn(p, "."); + p += plen; + if (plen > min_part) trunc = p-1; + if (*p) p++; + } + } while (trunc == NULL && --min_part != 0); + + if (trunc != NULL) { + do { + trunc[0] = trunc[1]; + } while (*trunc++); + trunc--; + } else { + trunc = strrchr(name, '.'); + if (trunc == NULL) error("internal error in shorten_name"); + if (trunc[1] == '\0') trunc--; /* force truncation */ + } + strcpy(trunc, z_suffix); +} + +/* ======================================================================== + * If compressing to a file, check if ofname is not ambiguous + * because the operating system truncates names. Otherwise, generate + * a new ofname and save the original name in the compressed file. + * If the compressed file already exists, ask for confirmation. + * The check for name truncation is made dynamically, because different + * file systems on the same OS might use different truncation rules (on SVR4 + * s5 truncates to 14 chars and ufs does not truncate). + * This function returns -1 if the file must be skipped, and + * updates save_orig_name if necessary. + * IN assertions: save_orig_name is already set if ofname has been + * already truncated because of NO_MULTIPLE_DOTS. The input file has + * already been open and istat is set. + */ +local int check_ofname() +{ + struct stat ostat; /* stat for ofname */ + +#ifdef ENAMETOOLONG + /* Check for strictly conforming Posix systems (which return ENAMETOOLONG + * instead of silently truncating filenames). + */ + errno = 0; + while (stat(ofname, &ostat) != 0) { + if (errno != ENAMETOOLONG) return 0; /* ofname does not exist */ + shorten_name(ofname); + } +#else + if (stat(ofname, &ostat) != 0) return 0; +#endif + /* Check for name truncation on existing file. Do this even on systems + * defining ENAMETOOLONG, because on most systems the strict Posix + * behavior is disabled by default (silent name truncation allowed). + */ + if (!decompress && name_too_long(ofname, &ostat)) { + shorten_name(ofname); + if (stat(ofname, &ostat) != 0) return 0; + } + + /* Check that the input and output files are different (could be + * the same by name truncation or links). + */ + if (same_file(&istat, &ostat)) { + if (strequ(ifname, ofname)) { + fprintf(stderr, "%s: %s: cannot %scompress onto itself\n", + progname, ifname, decompress ? "de" : ""); + } else { + fprintf(stderr, "%s: %s and %s are the same file\n", + progname, ifname, ofname); + } + exit_code = ERROR; + return ERROR; + } + /* Ask permission to overwrite the existing file */ + if (!force) { +#if 0 + char response[80]; + strcpy(response,"n"); + fprintf(stderr, "%s: %s already exists;", progname, ofname); + if (foreground && isatty(fileno(stdin))) { + fprintf(stderr, " do you wish to overwrite (y or n)? "); + (void)fgets(response, sizeof(response)-1, stdin); + } + if (tolow(*response) != 'y') { + fprintf(stderr, "\tnot overwritten\n"); +#endif + if (exit_code == OK) exit_code = WARNING; + return ERROR; +#if 0 + } +#endif + } + (void) chmod(ofname, 0777); + if (unlink(ofname)) { + fprintf(stderr, "%s: ", progname); + perror(ofname); + exit_code = ERROR; + return ERROR; + } + return OK; +} + + +/* ======================================================================== + * Set the access and modification times from the given stat buffer. + */ +local void reset_times (name, statb) + char *name; + struct stat *statb; +{ + struct utimbuf timep; + + /* Copy the time stamp */ + timep.actime = statb->st_atime; + timep.modtime = statb->st_mtime; + + /* Some systems (at least OS/2) do not support utime on directories */ + if (utime(name, &timep) && !S_ISDIR(statb->st_mode)) { + WARN((stderr, "%s: ", progname)); + if (!quiet) perror(ofname); + } +} + + +/* ======================================================================== + * Copy modes, times, ownership from input file to output file. + * IN assertion: to_stdout is false. + */ +local void copy_stat(ifstat) + struct stat *ifstat; +{ + if (decompress && time_stamp != 0 && ifstat->st_mtime != time_stamp) { + ifstat->st_mtime = time_stamp; + if (verbose > 1) { + fprintf(stderr, "%s: time stamp restored\n", ofname); + } + } + reset_times(ofname, ifstat); + + /* Copy the protection modes */ + if (chmod(ofname, ifstat->st_mode & 07777)) { + WARN((stderr, "%s: ", progname)); + if (!quiet) perror(ofname); + } + + chown(ofname, ifstat->st_uid, ifstat->st_gid); /* Copy ownership */ + + remove_ofname = 0; + /* It's now safe to remove the input file: */ + (void) chmod(ifname, 0777); + if (unlink(ifname)) { + WARN((stderr, "%s: ", progname)); + if (!quiet) perror(ifname); + } +} + +/* ======================================================================== + * Free all dynamically allocated variables and exit with the given code. + */ +local void do_exit(exitcode) + int exitcode; +{ + static int in_exit = 0; + + if (in_exit) exit(exitcode); + in_exit = 1; + if (env != NULL) free(env), env = NULL; + if (args != NULL) free((char*)args), args = NULL; + FREE(inbuf); + FREE(outbuf); + FREE(d_buf); + FREE(window); + FREE(tab_prefix); + exit(exitcode); +} + +/* ======================================================================== + * Signal and error handler. + */ +void abort_gzip() +{ + if (remove_ofname) { + close(ofd); + unlink (ofname); + } + do_exit(ERROR); +} diff --git a/usr/gzip/gzip.h b/usr/gzip/gzip.h new file mode 100644 index 0000000..7cd2fbd --- /dev/null +++ b/usr/gzip/gzip.h @@ -0,0 +1,298 @@ +/* gzip.h -- common declarations for all gzip modules + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if defined(__STDC__) || defined(PROTO) +# define OF(args) args +#else +# define OF(args) () +#endif + +#ifdef __STDC__ + typedef void *voidp; +#else + typedef char *voidp; +#endif + +/* I don't like nested includes, but the string and io functions are used + * too often + */ +#include <stdio.h> +#include <string.h> +#define memzero(s, n) memset ((voidp)(s), 0, (n)) + +#define local static + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +/* Return codes from gzip */ +#define OK 0 +#define ERROR 1 +#define WARNING 2 + +/* Compression methods (see algorithm.doc) */ +#define STORED 0 +#define COMPRESSED 1 +#define PACKED 2 +#define LZHED 3 +/* methods 4 to 7 reserved */ +#define DEFLATED 8 +#define MAX_METHODS 9 +extern int method; /* compression method */ + +/* To save memory for 16 bit systems, some arrays are overlaid between + * the various modules: + * deflate: prev+head window d_buf l_buf outbuf + * unlzw: tab_prefix tab_suffix stack inbuf outbuf + * inflate: window inbuf + * unpack: window inbuf prefix_len + * unlzh: left+right window c_table inbuf c_len + * For compression, input is done in window[]. For decompression, output + * is done in window except for unlzw. + */ + +#ifndef INBUFSIZ +# ifdef SMALL_MEM +# define INBUFSIZ 0x2000 /* input buffer size */ +# else +# define INBUFSIZ 0x8000 /* input buffer size */ +# endif +#endif +#define INBUF_EXTRA 64 /* required by unlzw() */ + +#ifndef OUTBUFSIZ +# ifdef SMALL_MEM +# define OUTBUFSIZ 8192 /* output buffer size */ +# else +# define OUTBUFSIZ 16384 /* output buffer size */ +# endif +#endif +#define OUTBUF_EXTRA 2048 /* required by unlzw() */ + +#ifndef DIST_BUFSIZE +# ifdef SMALL_MEM +# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */ +# else +# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ +# endif +#endif + +#ifdef DYN_ALLOC +# define EXTERN(type, array) extern type * near array +# define DECLARE(type, array, size) type * near array +# define ALLOC(type, array, size) { \ + array = (type*)fcalloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \ + if (array == NULL) error("insufficient memory"); \ + } +# define FREE(array) {if (array != NULL) fcfree(array), array=NULL;} +#else +# define EXTERN(type, array) extern type array[] +# define DECLARE(type, array, size) type array[size] +# define ALLOC(type, array, size) +# define FREE(array) +#endif + +EXTERN(uch, inbuf); /* input buffer */ +EXTERN(uch, outbuf); /* output buffer */ +EXTERN(ush, d_buf); /* buffer for distances, see trees.c */ +EXTERN(uch, window); /* Sliding window and suffix table (unlzw) */ +#define tab_suffix window +#ifndef MAXSEG_64K +# define tab_prefix prev /* hash link (see deflate.c) */ +# define head (prev+WSIZE) /* hash head (see deflate.c) */ + EXTERN(ush, tab_prefix); /* prefix code (see unlzw.c) */ +#else +# define tab_prefix0 prev +# define head tab_prefix1 + EXTERN(ush, tab_prefix0); /* prefix for even codes */ + EXTERN(ush, tab_prefix1); /* prefix for odd codes */ +#endif + +extern unsigned insize; /* valid bytes in inbuf */ +extern unsigned inptr; /* index of next byte to be processed in inbuf */ +extern unsigned outcnt; /* bytes in output buffer */ + +extern long bytes_in; /* number of input bytes */ +extern long bytes_out; /* number of output bytes */ +extern long header_bytes;/* number of bytes in gzip header */ + +#define isize bytes_in +/* for compatibility with old zip sources (to be cleaned) */ + +extern int ifd; /* input file descriptor */ +extern int ofd; /* output file descriptor */ +extern char ifname[]; /* input file name or "stdin" */ +extern char ofname[]; /* output file name or "stdout" */ +extern char *progname; /* program name */ + +extern time_t time_stamp; /* original time stamp (modification time) */ +extern long ifile_size; /* input file size, -1 for devices (debug only) */ + +typedef int file_t; /* Do not use stdio */ +#define NO_FILE (-1) /* in memory compression */ + + +#define PACK_MAGIC "\037\036" /* Magic header for packed files */ +#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ +#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ +#define LZH_MAGIC "\037\240" /* Magic header for SCO LZH Compress files*/ +#define PKZIP_MAGIC "\120\113\003\004" /* Magic header for pkzip files */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* internal file attribute */ +#define UNKNOWN 0xffff +#define BINARY 0 +#define ASCII 1 + +#ifndef WSIZE +# define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +extern int decrypt; /* flag to turn on decryption */ +extern int exit_code; /* program exit code */ +extern int verbose; /* be verbose (-v) */ +extern int quiet; /* be quiet (-q) */ +extern int level; /* compression level */ +extern int test; /* check .z file integrity */ +extern int to_stdout; /* output to stdout (-c) */ +extern int save_orig_name; /* set if original name must be saved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(0)) +#define try_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf(1)) + +/* put_byte is used for the compressed output, put_ubyte for the + * uncompressed output. However unlzw() uses window for its + * suffix table instead of its output buffer, so it does not use put_ubyte + * (to be cleaned up). + */ +#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ + flush_outbuf();} +#define put_ubyte(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\ + flush_window();} + +/* Output a 16 bit value, lsb first */ +#define put_short(w) \ +{ if (outcnt < OUTBUFSIZ-2) { \ + outbuf[outcnt++] = (uch) ((w) & 0xff); \ + outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \ + } else { \ + put_byte((uch)((w) & 0xff)); \ + put_byte((uch)((ush)(w) >> 8)); \ + } \ +} + +/* Output a 32 bit value to the bit stream, lsb first */ +#define put_long(n) { \ + put_short((n) & 0xffff); \ + put_short(((ulg)(n)) >> 16); \ +} + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +#define tolow(c) (isupper(c) ? (c)-'A'+'a' : (c)) /* force to lower case */ + +/* Macros for getting two-byte and four-byte header values */ +#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)) +#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#define WARN(msg) {if (!quiet) fprintf msg ; \ + if (exit_code == OK) exit_code = WARNING;} + + /* in zip.c: */ +extern int zip OF((int in, int out)); +extern int file_read OF((char *buf, unsigned size)); + + /* in unzip.c */ +extern int unzip OF((int in, int out)); +extern int check_zipfile OF((int in)); + + /* in gzip.c */ +void abort_gzip OF((void)); + + /* in deflate.c */ +void lm_init OF((int pack_level, ush *flags)); +ulg deflate OF((void)); + + /* in trees.c */ +void ct_init OF((ush *attr, int *method)); +int ct_tally OF((int dist, int lc)); +ulg flush_block OF((char *buf, ulg stored_len, int eof)); + + /* in bits.c */ +void bi_init OF((file_t zipfile)); +void send_bits OF((int value, int length)); +unsigned bi_reverse OF((unsigned value, int length)); +void bi_windup OF((void)); +void copy_block OF((char *buf, unsigned len, int header)); +extern int (*read_buf) OF((char *buf, unsigned size)); + + /* in util.c: */ +extern int copy OF((int in, int out)); +extern ulg updcrc OF((uch *s, unsigned n)); +extern void clear_bufs OF((void)); +extern int fill_inbuf OF((int eof_ok)); +extern void flush_outbuf OF((void)); +extern void flush_window OF((void)); +extern void write_buf OF((int fd, voidp buf, unsigned cnt)); +extern char *strlwr OF((char *s)); +extern char *basename OF((char *fname)); +extern void make_simple_name OF((char *name)); +extern char *add_envopt OF((int *argcp, char ***argvp, char *env)); +extern void error OF((char *m)); +extern void warn OF((char *a, char *b)); +extern void read_error OF((void)); +extern void write_error OF((void)); +extern void display_ratio OF((long num, long den, FILE *file)); +extern voidp xmalloc OF((unsigned int size)); + + /* in inflate.c */ +extern int inflate OF((void)); + +/* stuff from lzw.h */ +#ifndef BITS +# define BITS 16 +#endif diff --git a/usr/gzip/inflate.c b/usr/gzip/inflate.c new file mode 100644 index 0000000..a540538 --- /dev/null +++ b/usr/gzip/inflate.c @@ -0,0 +1,950 @@ +/* inflate.c -- Not copyrighted 1992 by Mark Adler + version c10p1, 10 January 1993 */ + +/* You can do whatever you like with this source file, though I would + prefer that if you modify it and redistribute it that you include + comments to that effect with your name and the date. Thank you. + [The history has been moved to the file ChangeLog.] + */ + +/* + Inflate deflated (PKZIP's method 8 compressed) data. The compression + method searches for as much of the current string of bytes (up to a + length of 258) in the previous 32K bytes. If it doesn't find any + matches (of at least length 3), it codes the next byte. Otherwise, it + codes the length of the matched string and its distance backwards from + the current position. There is a single Huffman code that codes both + single bytes (called "literals") and match lengths. A second Huffman + code codes the distance information, which follows a length code. Each + length or distance code actually represents a base value and a number + of "extra" (sometimes zero) bits to get to add to the base value. At + the end of each deflated block is a special end-of-block (EOB) literal/ + length code. The decoding process is basically: get a literal/length + code; if EOB then done; if a literal, emit the decoded byte; if a + length then get the distance and emit the referred-to bytes from the + sliding window of previously emitted data. + + There are (currently) three kinds of inflate blocks: stored, fixed, and + dynamic. The compressor deals with some chunk of data at a time, and + decides which method to use on a chunk-by-chunk basis. A chunk might + typically be 32K or 64K. If the chunk is uncompressible, then the + "stored" method is used. In this case, the bytes are simply stored as + is, eight bits per byte, with none of the above coding. The bytes are + preceded by a count, since there is no longer an EOB code. + + If the data is compressible, then either the fixed or dynamic methods + are used. In the dynamic method, the compressed data is preceded by + an encoding of the literal/length and distance Huffman codes that are + to be used to decode this block. The representation is itself Huffman + coded, and so is preceded by a description of that code. These code + descriptions take up a little space, and so for small blocks, there is + a predefined set of codes, called the fixed codes. The fixed method is + used if the block codes up smaller that way (usually for quite small + chunks), otherwise the dynamic method is used. In the latter case, the + codes are customized to the probabilities in the current block, and so + can code it much better than the pre-determined fixed codes. + + The Huffman codes themselves are decoded using a mutli-level table + lookup, in order to maximize the speed of decoding plus the speed of + building the decoding tables. See the comments below that precede the + lbits and dbits tuning parameters. + */ + + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarly, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: inflate.c,v 1.1 2002/08/18 00:59:21 hpa Exp $"; +#endif + +#include <sys/types.h> +#include <stdlib.h> + +#include "tailor.h" +#include "gzip.h" +#define slide window + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). + Valid extra bits are 0..13. e == 15 is EOB (end of block), e == 16 + means that v is a literal, 16 < e < 32 means that v is a pointer to + the next table, which codes e - 16 bits, and lastly e == 99 indicates + an unused code. If a code with e == 99 is looked up, this implies an + error in the data. */ +struct huft { + uch e; /* number of extra bits or operation */ + uch b; /* number of bits in this code or subcode */ + union { + ush n; /* literal, length base, or distance base */ + struct huft *t; /* pointer to next level of table */ + } v; +}; + + +/* Function prototypes */ +int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *, + struct huft **, int *)); +int huft_free OF((struct huft *)); +int inflate_codes OF((struct huft *, struct huft *, int, int)); +int inflate_stored OF((void)); +int inflate_fixed OF((void)); +int inflate_dynamic OF((void)); +int inflate_block OF((int *)); +int inflate OF((void)); + + +/* The inflate algorithm uses a sliding 32K byte window on the uncompressed + stream to find repeated byte strings. This is implemented here as a + circular buffer. The index is updated simply by incrementing and then + and'ing with 0x7fff (32K-1). */ +/* It is left to other modules to supply the 32K area. It is assumed + to be usable as if it were declared "uch slide[32768];" or as just + "uch *slide;" and then malloc'ed in the latter case. The definition + must be in unzip.h, included above. */ +/* unsigned wp; current position in slide */ +#define wp outcnt +#define flush_output(w) (wp=(w),flush_window()) + +/* Tables for deflate from PKZIP's appnote.txt. */ +static unsigned border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; +static ush cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* note: see note #13 above about the 258 in this list. */ +static ush cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */ +static ush cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +static ush cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + + +/* Macros for inflate() bit peeking and grabbing. + The usage is: + + NEEDBITS(j) + x = b & mask_bits[j]; + DUMPBITS(j) + + where NEEDBITS makes sure that b has at least j bits in it, and + DUMPBITS removes the bits from b. The macros use the variable k + for the number of bits in b. Normally, b and k are register + variables for speed, and are initialized at the beginning of a + routine that uses these macros from a global bit buffer and count. + + If we assume that EOB will be the longest code, then we will never + ask for bits with NEEDBITS that are beyond the end of the stream. + So, NEEDBITS should not read any more bytes than are needed to + meet the request. Then no bytes need to be "returned" to the buffer + at the end of the last block. + + However, this assumption is not true for fixed blocks--the EOB code + is 7 bits, but the other literal/length codes can be 8 or 9 bits. + (The EOB code is shorter than other codes because fixed blocks are + generally short. So, while a block always has an EOB, many other + literal/length codes have a significantly lower probability of + showing up at all.) However, by making the first table have a + lookup of seven bits, the EOB code will be found in that first + lookup, and so will not require that too many bits be pulled from + the stream. + */ + +ulg bb; /* bit buffer */ +unsigned bk; /* bits in bit buffer */ + +ush mask_bits[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +#ifdef CRYPT + uch cc; +# define NEXTBYTE() \ + (decrypt ? (cc = get_byte(), cc) : get_byte()) +#else +# define NEXTBYTE() (uch)get_byte() +#endif +#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}} +#define DUMPBITS(n) {b>>=(n);k-=(n);} + + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +int lbits = 9; /* bits in base literal/length lookup table */ +int dbits = 6; /* bits in base distance lookup table */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ +#define BMAX 16 /* maximum bit length of any code (16 for explode) */ +#define N_MAX 288 /* maximum number of codes in any set */ + + +unsigned hufts; /* track memory usage */ + + +int huft_build(b, n, s, d, e, t, m) +unsigned *b; /* code lengths in bits (all assumed <= BMAX) */ +unsigned n; /* number of codes (assumed <= N_MAX) */ +unsigned s; /* number of simple-valued codes (0..s-1) */ +ush *d; /* list of base values for non-simple codes */ +ush *e; /* list of extra bits for non-simple codes */ +struct huft **t; /* result: starting table */ +int *m; /* maximum lookup bits, returns actual */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. */ +{ + unsigned a; /* counter for codes of length k */ + unsigned c[BMAX+1]; /* bit length count table */ + unsigned f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register unsigned i; /* counter, current code */ + register unsigned j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register unsigned *p; /* pointer into c[], b[], or v[] */ + register struct huft *q; /* points to current table */ + struct huft r; /* table entry for structure assignment */ + struct huft *u[BMAX]; /* table stack */ + unsigned v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + unsigned x[BMAX+1]; /* bit offsets, then code stack */ + unsigned *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + unsigned z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + memzero(c, sizeof(c)); + p = b; i = n; + do { + Tracecv(*p, (stderr, (n-i >= ' ' && n-i <= '~' ? "%c %d\n" : "0x%x %d\n"), + n-i, *p)); + c[*p]++; /* assume all entries <= BMAX */ + p++; /* Can't combine with above line (Solaris bug) */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (struct huft *)NULL; + *m = 0; + return 0; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((unsigned)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((unsigned)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return 2; /* bad input: more codes than bits */ + if ((y -= c[i]) < 0) + return 2; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (struct huft *)NULL; /* just to keep compilers happy */ + q = (struct huft *)NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (unsigned)l ? (unsigned)l : z; /* upper limit on table size */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (struct huft *)malloc((z + 1)*sizeof(struct huft))) == + (struct huft *)NULL) + { + if (h) + huft_free(u[0]); + return 3; /* not enough memory */ + } + hufts += z + 1; /* track memory usage */ + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->v.t)) = (struct huft *)NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.b = (uch)l; /* bits to dump before this table */ + r.e = (uch)(16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.b = (uch)(k - w); + if (p >= v + n) + r.e = 99; /* out of values--invalid code */ + else if (*p < s) + { + r.e = (uch)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ + r.v.n = (ush)(*p); /* simple code is just the value */ + p++; /* one compiler does not like *p++ */ + } + else + { + r.e = (uch)e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return true (1) if we were given an incomplete table */ + return y != 0 && g != 1; +} + + + +int huft_free(t) +struct huft *t; /* table to free */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register struct huft *p, *q; + + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != (struct huft *)NULL) + { + q = (--p)->v.t; + free((char*)p); + p = q; + } + return 0; +} + + +int inflate_codes(tl, td, bl, bd) +struct huft *tl, *td; /* literal/length and distance decoder tables */ +int bl, bd; /* number of bits decoded by tl[] and td[] */ +/* inflate (decompress) the codes in a deflated (compressed) block. + Return an error code or zero if it all goes ok. */ +{ + register unsigned e; /* table entry flag/number of extra bits */ + unsigned n, d; /* length and index for copy */ + unsigned w; /* current window position */ + struct huft *t; /* pointer to table entry */ + unsigned ml, md; /* masks for bl and bd bits */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = wp; /* initialize window position */ + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + for (;;) /* do until end of block */ + { + NEEDBITS((unsigned)bl) + if ((e = (t = tl + ((unsigned)b & ml))->e) > 16) + do { + if (e == 99) + return 1; + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); + DUMPBITS(t->b) + if (e == 16) /* then it's a literal */ + { + slide[w++] = (uch)t->v.n; + Tracevv((stderr, "%c", slide[w-1])); + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + } + else /* it's an EOB or a length */ + { + /* exit if end of block */ + if (e == 15) + break; + + /* get length of block to copy */ + NEEDBITS(e) + n = t->v.n + ((unsigned)b & mask_bits[e]); + DUMPBITS(e); + + /* decode distance of block to copy */ + NEEDBITS((unsigned)bd) + if ((e = (t = td + ((unsigned)b & md))->e) > 16) + do { + if (e == 99) + return 1; + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); + DUMPBITS(t->b) + NEEDBITS(e) + d = w - t->v.n - ((unsigned)b & mask_bits[e]); + DUMPBITS(e) + Tracevv((stderr,"\\[%d,%d]", w-d, n)); + + /* do the copy */ + do { + n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e); +#if !defined(NOMEMCPY) && !defined(DEBUG) + if (w - d >= e) /* (this test assumes unsigned comparison) */ + { + memcpy(slide + w, slide + d, e); + w += e; + d += e; + } + else /* do it slow to avoid memcpy() overlap */ +#endif /* !NOMEMCPY */ + do { + slide[w++] = slide[d++]; + Tracevv((stderr, "%c", slide[w-1])); + } while (--e); + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + } while (n); + } + } + + + /* restore the globals from the locals */ + wp = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + + /* done */ + return 0; +} + + + +int inflate_stored() +/* "decompress" an inflated type 0 (stored) block. */ +{ + unsigned n; /* number of bytes in block */ + unsigned w; /* current window position */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = wp; /* initialize window position */ + + + /* go to byte boundary */ + n = k & 7; + DUMPBITS(n); + + + /* get the length and its complement */ + NEEDBITS(16) + n = ((unsigned)b & 0xffff); + DUMPBITS(16) + NEEDBITS(16) + if (n != (unsigned)((~b) & 0xffff)) + return 1; /* error in compressed data */ + DUMPBITS(16) + + + /* read and output the compressed data */ + while (n--) + { + NEEDBITS(8) + slide[w++] = (uch)b; + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + DUMPBITS(8) + } + + + /* restore the globals from the locals */ + wp = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + return 0; +} + + + +int inflate_fixed() +/* decompress an inflated type 1 (fixed Huffman codes) block. We should + either replace this with a custom decoder, or at least precompute the + Huffman tables. */ +{ + int i; /* temporary variable */ + struct huft *tl; /* literal/length code table */ + struct huft *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned l[288]; /* length list for huft_build */ + + + /* set up literal table */ + for (i = 0; i < 144; i++) + l[i] = 8; + for (; i < 256; i++) + l[i] = 9; + for (; i < 280; i++) + l[i] = 7; + for (; i < 288; i++) /* make a complete, but wrong code set */ + l[i] = 8; + bl = 7; + if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0) + return i; + + + /* set up distance table */ + for (i = 0; i < 30; i++) /* make an incomplete code set */ + l[i] = 5; + bd = 5; + if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1) + { + huft_free(tl); + return i; + } + + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; +} + + + +int inflate_dynamic() +/* decompress an inflated type 2 (dynamic Huffman codes) block. */ +{ + int i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + struct huft *tl; /* literal/length code table */ + struct huft *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ +#ifdef PKZIP_BUG_WORKAROUND + unsigned ll[288+32]; /* literal/length and distance code lengths */ +#else + unsigned ll[286+30]; /* literal/length and distance code lengths */ +#endif + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local bit buffer */ + b = bb; + k = bk; + + + /* read in table lengths */ + NEEDBITS(5) + nl = 257 + ((unsigned)b & 0x1f); /* number of literal/length codes */ + DUMPBITS(5) + NEEDBITS(5) + nd = 1 + ((unsigned)b & 0x1f); /* number of distance codes */ + DUMPBITS(5) + NEEDBITS(4) + nb = 4 + ((unsigned)b & 0xf); /* number of bit length codes */ + DUMPBITS(4) +#ifdef PKZIP_BUG_WORKAROUND + if (nl > 288 || nd > 32) +#else + if (nl > 286 || nd > 30) +#endif + return 1; /* bad lengths */ + + + /* read in bit-length-code lengths */ + for (j = 0; j < nb; j++) + { + NEEDBITS(3) + ll[border[j]] = (unsigned)b & 7; + DUMPBITS(3) + } + for (; j < 19; j++) + ll[border[j]] = 0; + + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) + { + if (i == 1) + huft_free(tl); + return i; /* incomplete code set */ + } + + + /* read in literal and distance code lengths */ + n = nl + nd; + m = mask_bits[bl]; + i = l = 0; + while ((unsigned)i < n) + { + NEEDBITS((unsigned)bl) + j = (td = tl + ((unsigned)b & m))->b; + DUMPBITS(j) + j = td->v.n; + if (j < 16) /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + else if (j == 16) /* repeat last length 3 to 6 times */ + { + NEEDBITS(2) + j = 3 + ((unsigned)b & 3); + DUMPBITS(2) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = l; + } + else if (j == 17) /* 3 to 10 zero length codes */ + { + NEEDBITS(3) + j = 3 + ((unsigned)b & 7); + DUMPBITS(3) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + else /* j == 18: 11 to 138 zero length codes */ + { + NEEDBITS(7) + j = 11 + ((unsigned)b & 0x7f); + DUMPBITS(7) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + } + + + /* free decoding table for trees */ + huft_free(tl); + + + /* restore the global bit buffer */ + bb = b; + bk = k; + + + /* build the decoding tables for literal/length and distance codes */ + bl = lbits; + if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) + { + if (i == 1) { + fprintf(stderr, " incomplete literal tree\n"); + huft_free(tl); + } + return i; /* incomplete code set */ + } + bd = dbits; + if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) + { + if (i == 1) { + fprintf(stderr, " incomplete distance tree\n"); +#ifdef PKZIP_BUG_WORKAROUND + i = 0; + } +#else + huft_free(td); + } + huft_free(tl); + return i; /* incomplete code set */ +#endif + } + + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; +} + + + +int inflate_block(e) +int *e; /* last block flag */ +/* decompress an inflated block */ +{ + unsigned t; /* block type */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local bit buffer */ + b = bb; + k = bk; + + + /* read in last block bit */ + NEEDBITS(1) + *e = (int)b & 1; + DUMPBITS(1) + + + /* read in block type */ + NEEDBITS(2) + t = (unsigned)b & 3; + DUMPBITS(2) + + + /* restore the global bit buffer */ + bb = b; + bk = k; + + + /* inflate that block type */ + if (t == 2) + return inflate_dynamic(); + if (t == 0) + return inflate_stored(); + if (t == 1) + return inflate_fixed(); + + + /* bad block type */ + return 2; +} + + + +int inflate() +/* decompress an inflated entry */ +{ + int e; /* last block flag */ + int r; /* result code */ + unsigned h; /* maximum struct huft's malloc'ed */ + + + /* initialize window, bit buffer */ + wp = 0; + bk = 0; + bb = 0; + + + /* decompress until the last block */ + h = 0; + do { + hufts = 0; + if ((r = inflate_block(&e)) != 0) + return r; + if (hufts > h) + h = hufts; + } while (!e); + + /* Undo too much lookahead. The next read will be byte aligned so we + * can discard unused bits in the last meaningful byte. + */ + while (bk >= 8) { + bk -= 8; + inptr--; + } + + /* flush out slide */ + flush_output(wp); + + + /* return success */ +#ifdef DEBUG + fprintf(stderr, "<%u> ", h); +#endif /* DEBUG */ + return 0; +} diff --git a/usr/gzip/revision.h b/usr/gzip/revision.h new file mode 100644 index 0000000..736020b --- /dev/null +++ b/usr/gzip/revision.h @@ -0,0 +1,11 @@ +/* revision.h -- define the version number + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#define VERSION "1.2.4" +#define PATCHLEVEL 0 +#define REVDATE "18 Aug 93" + +/* $Id: revision.h,v 1.1 2002/08/18 00:59:21 hpa Exp $ */ diff --git a/usr/gzip/tailor.h b/usr/gzip/tailor.h new file mode 100644 index 0000000..849e589 --- /dev/null +++ b/usr/gzip/tailor.h @@ -0,0 +1,50 @@ +/* tailor.h -- target dependent definitions + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* The target dependent definitions should be defined here only. + * The target dependent functions should be defined in tailor.c. + */ + +/* $Id: tailor.h,v 1.1 2002/08/18 00:59:21 hpa Exp $ */ + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#define PATH_SEP '/' + +#ifndef casemap +# define casemap(c) (c) +#endif + +#ifndef OPTIONS_VAR +# define OPTIONS_VAR "GZIP" +#endif + +#ifndef Z_SUFFIX +# define Z_SUFFIX ".gz" +#endif + +#define MAX_SUFFIX 30 + +#ifndef MIN_PART +# define MIN_PART 3 + /* keep at least MIN_PART chars between dots in a file name. */ +#endif + +#ifndef RECORD_IO +# define RECORD_IO 0 +#endif + +#ifndef get_char +# define get_char() get_byte() +#endif + +#ifndef put_char +# define put_char(c) put_byte(c) +#endif diff --git a/usr/gzip/trees.c b/usr/gzip/trees.c new file mode 100644 index 0000000..75efc97 --- /dev/null +++ b/usr/gzip/trees.c @@ -0,0 +1,1075 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* + * PURPOSE + * + * Encode various sets of source values using variable-length + * binary code trees. + * + * DISCUSSION + * + * The PKZIP "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in the ZIP file in a compressed form + * which is itself a Huffman encoding of the lengths of + * all the code strings (in ascending order by source values). + * The actual code strings are reconstructed from the lengths in + * the UNZIP process, as described in the "application note" + * (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program. + * + * REFERENCES + * + * Lynch, Thomas J. + * Data Compression: Techniques and Applications, pp. 53-55. + * Lifetime Learning Publications, 1985. ISBN 0-534-03418-7. + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + * + * INTERFACE + * + * void ct_init (ush *attr, int *methodp) + * Allocate the match buffer, initialize the various tables and save + * the location of the internal file attribute (ascii/binary) and + * method (DEFLATE/STORE) + * + * void ct_tally (int dist, int lc); + * Save the match info and tally the frequency counts. + * + * long flush_block (char *buf, ulg stored_len, int eof) + * Determine the best encoding for the current block: dynamic trees, + * static trees or store, and output the encoded block to the zip + * file. Returns the total compressed length for the file so far. + * + */ + +#include <ctype.h> + +#include "tailor.h" +#include "gzip.h" + +#ifdef RCSID +static char rcsid[] = "$Id: trees.c,v 1.1 2002/08/18 00:59:21 hpa Exp $"; +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + + +local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#ifndef LIT_BUFSIZE +# ifdef SMALL_MEM +# define LIT_BUFSIZE 0x2000 +# else +# ifdef MEDIUM_MEM +# define LIT_BUFSIZE 0x4000 +# else +# define LIT_BUFSIZE 0x8000 +# endif +# endif +#endif +#ifndef DIST_BUFSIZE +# define DIST_BUFSIZE LIT_BUFSIZE +#endif +/* Sizes of match buffers for literals/lengths and distances. There are + * 4 reasons for limiting LIT_BUFSIZE to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input data is + * still in the window so we can still emit a stored block even when input + * comes from standard input. (This can also be done for all blocks if + * LIT_BUFSIZE is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting trees + * more frequently. + * - I can't count above 4 + * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save + * memory at the expense of compression). Some optimizations would be possible + * if we rely on DIST_BUFSIZE == LIT_BUFSIZE. + */ +#if LIT_BUFSIZE > INBUFSIZ + error cannot overlay l_buf and inbuf +#endif + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* =========================================================================== + * Local data + */ + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +local ct_data dyn_ltree[HEAP_SIZE]; /* literal and length tree */ +local ct_data dyn_dtree[2*D_CODES+1]; /* distance tree */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see ct_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +local ct_data bl_tree[2*BL_CODES+1]; +/* Huffman tree for the bit lengths */ + +typedef struct tree_desc { + ct_data *dyn_tree; /* the dynamic tree */ + ct_data *static_tree; /* corresponding static tree or NULL */ + int *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ + int max_code; /* largest code with non zero frequency */ +} tree_desc; + +local tree_desc l_desc = +{dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0}; + +local tree_desc d_desc = +{dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0}; + +local tree_desc bl_desc = +{bl_tree, (ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0}; + + +local ush bl_count[MAX_BITS+1]; +/* number of codes at each bit length for an optimal tree */ + +local uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +local int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ +local int heap_len; /* number of elements in the heap */ +local int heap_max; /* element of largest frequency */ +/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + +local uch depth[2*L_CODES+1]; +/* Depth of each subtree used as tie breaker for trees of equal frequency */ + +local uch length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local uch dist_code[512]; +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#define l_buf inbuf +/* DECLARE(uch, l_buf, LIT_BUFSIZE); buffer for literals or lengths */ + +/* DECLARE(ush, d_buf, DIST_BUFSIZE); buffer for distances */ + +local uch flag_buf[(LIT_BUFSIZE/8)]; +/* flag_buf is a bit array distinguishing literals from lengths in + * l_buf, thus indicating the presence or absence of a distance. + */ + +local unsigned last_lit; /* running index in l_buf */ +local unsigned last_dist; /* running index in d_buf */ +local unsigned last_flags; /* running index in flag_buf */ +local uch flags; /* current flags not yet saved in flag_buf */ +local uch flag_bit; /* current bit used in flags */ +/* bits are filled in flags starting at bit 0 (least significant). + * Note: these flags are overkill in the current code since we don't + * take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + */ + +local ulg opt_len; /* bit length of current block with optimal trees */ +local ulg static_len; /* bit length of current block with static trees */ + +local ulg compressed_len; /* total bit length of compressed file */ + +local ulg input_len; /* total byte length of input file */ +/* input_len is for debugging only since we can get it by other means. */ + +ush *file_type; /* pointer to UNKNOWN, BINARY or ASCII */ +int *file_method; /* pointer to DEFLATE or STORE */ + +#ifdef DEBUG +extern ulg bits_sent; /* bit length of the compressed data */ +extern long isize; /* byte length of input file */ +#endif + +extern long block_start; /* window offset of current block */ +extern unsigned strstart; /* window offset of current string */ + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void init_block OF((void)); +local void pqdownheap OF((ct_data *tree, int k)); +local void gen_bitlen OF((tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code)); +local void build_tree OF((tree_desc *desc)); +local void scan_tree OF((ct_data *tree, int max_code)); +local void send_tree OF((ct_data *tree, int max_code)); +local int build_bl_tree OF((void)); +local void send_all_trees OF((int lcodes, int dcodes, int blcodes)); +local void compress_block OF((ct_data *ltree, ct_data *dtree)); +local void set_file_type OF((void)); + + +#ifndef DEBUG +# define send_code(c, tree) send_bits(tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(c, tree) \ + { if (verbose>1) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(tree[c].Code, tree[c].Len); } +#endif + +#define d_code(dist) \ + ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ + +#define MAX(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +void ct_init(attr, methodp) + ush *attr; /* pointer to internal file attribute */ + int *methodp; /* pointer to compression method */ +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + file_type = attr; + file_method = methodp; + compressed_len = input_len = 0L; + + if (static_dtree[0].Len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1<<extra_lbits[code]); n++) { + length_code[length++] = (uch)code; + } + } + Assert (length == 256, "ct_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + length_code[length-1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + dist_code[dist++] = (uch)code; + } + } + Assert (dist == 256, "ct_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block() +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) bl_tree[n].Freq = 0; + + dyn_ltree[END_BLOCK].Freq = 1; + opt_len = static_len = 0L; + last_lit = last_dist = last_flags = 0; + flags = 0; flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = heap[SMALLEST]; \ + heap[SMALLEST] = heap[heap_len--]; \ + pqdownheap(tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(tree, k) + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = heap[k]; + int j = k << 1; /* left son of k */ + while (j <= heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < heap_len && smaller(tree, heap[j+1], heap[j])) j++; + + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, heap[j])) break; + + /* Exchange v with the smallest son */ + heap[k] = heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(desc) + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[heap[heap_max]].Len = 0; /* root of the heap */ + + for (h = heap_max+1; h < HEAP_SIZE; h++) { + n = heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + opt_len += (ulg)f * (bits + xbits); + if (stree) static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (bl_count[bits] == 0) bits--; + bl_count[bits]--; /* move one leaf down the tree */ + bl_count[bits+1] += 2; /* move one overflow item as its brother */ + bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = bl_count[bits]; + while (n != 0) { + m = heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + opt_len += ((long)bits-(long)tree[m].Len)*(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracec(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +local void build_tree(desc) + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + heap_len = 0, heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + heap[++heap_len] = max_code = n; + depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (heap_len < 2) { + int new = heap[++heap_len] = (max_code < 2 ? ++max_code : 0); + tree[new].Freq = 1; + depth[new] = 0; + opt_len--; if (stree) static_len -= stree[new].Len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = heap_len/2; n >= 1; n--) pqdownheap(tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = heap[SMALLEST]; /* m = node of next least frequency */ + + heap[--heap_max] = n; /* keep the nodes sorted by frequency */ + heap[--heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + depth[node] = (uch) (MAX(depth[n], depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + heap[SMALLEST] = node++; + pqdownheap(tree, SMALLEST); + + } while (heap_len >= 2); + + heap[--heap_max] = heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen((tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +local void scan_tree (tree, max_code) + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) bl_tree[curlen].Freq++; + bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + bl_tree[REPZ_3_10].Freq++; + } else { + bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (tree, max_code) + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(curlen, bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(curlen, bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(REP_3_6, bl_tree); send_bits(count-3, 2); + + } else if (count <= 10) { + send_code(REPZ_3_10, bl_tree); send_bits(count-3, 3); + + } else { + send_code(REPZ_11_138, bl_tree); send_bits(count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree() +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree((ct_data *)dyn_ltree, l_desc.max_code); + scan_tree((ct_data *)dyn_dtree, d_desc.max_code); + + /* Build the bit length tree: */ + build_tree((tree_desc *)(&bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", opt_len, static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(lcodes, dcodes, blcodes) + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", bits_sent)); + + send_tree((ct_data *)dyn_ltree, lcodes-1); /* send the literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", bits_sent)); + + send_tree((ct_data *)dyn_dtree, dcodes-1); /* send the distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", bits_sent)); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +ulg flush_block(buf, stored_len, eof) + char *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + flag_buf[last_flags] = flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*file_type == (ush)UNKNOWN) set_file_type(); + + /* Construct the literal and distance trees */ + build_tree((tree_desc *)(&l_desc)); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len)); + + build_tree((tree_desc *)(&d_desc)); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", opt_len, static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (opt_len+3+7)>>3; + static_lenb = (static_len+3+7)>>3; + input_len += stored_len; /* for debugging only */ + + Trace((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, opt_len, static_lenb, static_len, stored_len, + last_lit, last_dist)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + /* If compression failed and this is the first and last block, + * and if the zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ +#ifdef FORCE_METHOD + if (level == 1 && eof && compressed_len == 0L) { /* force stored file */ +#else + if (stored_len <= opt_lenb && eof && compressed_len == 0L && seekable()) { +#endif + /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ + if (buf == (char*)0) error ("block vanished"); + + copy_block(buf, (unsigned)stored_len, 0); /* without header */ + compressed_len = stored_len << 3; + *file_method = STORED; + +#ifdef FORCE_METHOD + } else if (level == 2 && buf != (char*)0) { /* force stored block */ +#else + } else if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits((STORED_BLOCK<<1)+eof, 3); /* send block type */ + compressed_len = (compressed_len + 3 + 7) & ~7L; + compressed_len += (stored_len + 4) << 3; + + copy_block(buf, (unsigned)stored_len, 1); /* with header */ + +#ifdef FORCE_METHOD + } else if (level == 3) { /* force static trees */ +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits((STATIC_TREES<<1)+eof, 3); + compress_block((ct_data *)static_ltree, (ct_data *)static_dtree); + compressed_len += 3 + static_len; + } else { + send_bits((DYN_TREES<<1)+eof, 3); + send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); + compress_block((ct_data *)dyn_ltree, (ct_data *)dyn_dtree); + compressed_len += 3 + opt_len; + } + Assert (compressed_len == bits_sent, "bad compressed size"); + init_block(); + + if (eof) { + Assert (input_len == isize, "bad input size"); + bi_windup(); + compressed_len += 7; /* align on byte boundary */ + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", compressed_len>>3, + compressed_len-7*eof)); + + return compressed_len >> 3; +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ct_tally (dist, lc) + int dist; /* distance of matched string */ + int lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + l_buf[last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + dyn_ltree[lc].Freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + dyn_ltree[length_code[lc]+LITERALS+1].Freq++; + dyn_dtree[d_code(dist)].Freq++; + + d_buf[last_dist++] = (ush)dist; + flags |= flag_bit; + } + flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((last_lit & 7) == 0) { + flag_buf[last_flags++] = flags; + flags = 0, flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if (level > 2 && (last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)last_lit*8L; + ulg in_length = (ulg)strstart-block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)dyn_dtree[dcode].Freq*(5L+extra_dbits[dcode]); + } + out_length >>= 3; + Trace((stderr,"\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + last_lit, last_dist, in_length, out_length, + 100L - out_length*100L/in_length)); + if (last_dist < last_lit/2 && out_length < in_length/2) return 1; + } + return (last_lit == LIT_BUFSIZE-1 || last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(ltree, dtree) + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (last_lit != 0) do { + if ((lx & 7) == 0) flag = flag_buf[fx++]; + lc = l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = length_code[lc]; + send_code(code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(lc, extra); /* send the extra length bits */ + } + dist = d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < last_lit); + + send_code(END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_file_type() +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += dyn_ltree[n++].Freq; + *file_type = bin_freq > (ascii_freq >> 2) ? BINARY : ASCII; + if (*file_type == BINARY && translate_eol) { + warn("-l used on binary file", ""); + } +} diff --git a/usr/gzip/unzip.c b/usr/gzip/unzip.c new file mode 100644 index 0000000..c8e14a3 --- /dev/null +++ b/usr/gzip/unzip.c @@ -0,0 +1,77 @@ +/* unzip.c -- decompress files in gzip or pkzip format. + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + * + * The code in this file is derived from the file funzip.c written + * and put in the public domain by Mark Adler. + */ + +/* + This version can extract files in gzip format. + Only the first entry is extracted, and it has to be + either deflated or stored. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: unzip.c,v 1.1 2002/08/18 00:59:21 hpa Exp $"; +#endif + +#include "tailor.h" +#include "gzip.h" + +/* =========================================================================== + * Unzip in to out. This routine works on gzip files only. + * + * IN assertions: the buffer inbuf contains already the beginning of + * the compressed data, from offsets inptr to insize-1 included. + * The magic header has already been checked. The output buffer is cleared. + */ +int unzip(in, out) + int in, out; /* input and output file descriptors */ +{ + ulg orig_crc = 0; /* original crc */ + ulg orig_len = 0; /* original uncompressed length */ + int n; + uch buf[8]; /* extended local header */ + + ifd = in; + ofd = out; + + updcrc(NULL, 0); /* initialize crc */ + + /* Decompress */ + if (method == DEFLATED) { + + int res = inflate(); + + if (res == 3) { + error("out of memory"); + } else if (res != 0) { + error("invalid compressed data--format violated"); + } + + } else { + error("internal error, invalid method"); + } + + /* Get the crc and original length */ + /* crc32 (see algorithm.doc) + * uncompressed input size modulo 2^32 + */ + for (n = 0; n < 8; n++) { + buf[n] = (uch)get_byte(); /* may cause an error if EOF */ + } + orig_crc = LG(buf); + orig_len = LG(buf+4); + + /* Validate decompression */ + if (orig_crc != updcrc(outbuf, 0)) { + error("invalid compressed data--crc error"); + } + if (orig_len != (ulg)bytes_out) { + error("invalid compressed data--length error"); + } + + return OK; +} diff --git a/usr/gzip/util.c b/usr/gzip/util.c new file mode 100644 index 0000000..22ff6e5 --- /dev/null +++ b/usr/gzip/util.c @@ -0,0 +1,372 @@ +/* util.c -- utility functions for gzip support + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: util.c,v 1.1 2002/08/18 00:59:21 hpa Exp $"; +#endif + +#include <ctype.h> +#include <errno.h> +#include <sys/types.h> + +#include "tailor.h" + +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> + +#include "gzip.h" + +extern ulg crc_32_tab[]; /* crc table, defined below */ + +/* =========================================================================== + * Copy input to output unchanged: zcat == cat with --force. + * IN assertion: insize bytes have already been read in inbuf. + */ +int copy(in, out) + int in, out; /* input and output file descriptors */ +{ + errno = 0; + while (insize != 0 && (int)insize != EOF) { + write_buf(out, (char*)inbuf, insize); + bytes_out += insize; + insize = read(in, (char*)inbuf, INBUFSIZ); + } + if ((int)insize == EOF && errno != 0) { + read_error(); + } + bytes_in = bytes_out; + return OK; +} + +/* =========================================================================== + * Run a set of bytes through the crc shift register. If s is a NULL + * pointer, then initialize the crc shift register contents instead. + * Return the current crc in either case. + */ +ulg updcrc(s, n) + uch *s; /* pointer to bytes to pump through */ + unsigned n; /* number of bytes in s[] */ +{ + register ulg c; /* temporary variable */ + + static ulg crc = (ulg)0xffffffffL; /* shift register contents */ + + if (s == NULL) { + c = 0xffffffffL; + } else { + c = crc; + if (n) do { + c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8); + } while (--n); + } + crc = c; + return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */ +} + +/* =========================================================================== + * Clear input and output buffers + */ +void clear_bufs() +{ + outcnt = 0; + insize = inptr = 0; + bytes_in = bytes_out = 0L; +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty. + */ +int fill_inbuf(eof_ok) + int eof_ok; /* set if EOF acceptable as a result */ +{ + int len; + + /* Read as much as possible */ + insize = 0; + errno = 0; + do { + len = read(ifd, (char*)inbuf+insize, INBUFSIZ-insize); + if (len == 0 || len == EOF) break; + insize += len; + } while (insize < INBUFSIZ); + + if (insize == 0) { + if (eof_ok) return EOF; + read_error(); + } + bytes_in += (ulg)insize; + inptr = 1; + return inbuf[0]; +} + +/* =========================================================================== + * Write the output buffer outbuf[0..outcnt-1] and update bytes_out. + * (used for the compressed data only) + */ +void flush_outbuf() +{ + if (outcnt == 0) return; + + write_buf(ofd, (char *)outbuf, outcnt); + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +void flush_window() +{ + if (outcnt == 0) return; + updcrc(window, outcnt); + + if (!test) { + write_buf(ofd, (char *)window, outcnt); + } + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +/* =========================================================================== + * Does the same as write(), but also handles partial pipe writes and checks + * for error return. + */ +void write_buf(fd, buf, cnt) + int fd; + voidp buf; + unsigned cnt; +{ + unsigned n; + + while ((n = write(fd, buf, cnt)) != cnt) { + if (n == (unsigned)(-1)) { + write_error(); + } + cnt -= n; + buf = (voidp)((char*)buf+n); + } +} + +/* ======================================================================== + * Put string s in lower case, return s. + */ +char *strlwr(s) + char *s; +{ + char *t; + for (t = s; *t; t++) *t = tolow(*t); + return s; +} + +/* ======================================================================== + * Return the base name of a file (remove any directory prefix and + * any version suffix). For systems with file names that are not + * case sensitive, force the base name to lower case. + */ +char *basename(fname) + char *fname; +{ + char *p; + + if ((p = strrchr(fname, PATH_SEP)) != NULL) fname = p+1; + if (casemap('A') == 'a') strlwr(fname); + return fname; +} + +/* ======================================================================== + * Add an environment variable (if any) before argv, and update argc. + * Return the expanded environment variable to be freed later, or NULL + * if no options were added to argv. + */ +#define SEPARATOR " \t" /* separators in env variable */ + +char *add_envopt(argcp, argvp, env) + int *argcp; /* pointer to argc */ + char ***argvp; /* pointer to argv */ + char *env; /* name of environment variable */ +{ + char *p; /* running pointer through env variable */ + char **oargv; /* runs through old argv array */ + char **nargv; /* runs through new argv array */ + int oargc = *argcp; /* old argc */ + int nargc = 0; /* number of arguments in env variable */ + + env = (char*)getenv(env); + if (env == NULL) return NULL; + + p = (char*)xmalloc(strlen(env)+1); + env = strcpy(p, env); /* keep env variable intact */ + + for (p = env; *p; nargc++ ) { /* move through env */ + p += strspn(p, SEPARATOR); /* skip leading separators */ + if (*p == '\0') break; + + p += strcspn(p, SEPARATOR); /* find end of word */ + if (*p) *p++ = '\0'; /* mark it */ + } + if (nargc == 0) { + free(env); + return NULL; + } + *argcp += nargc; + /* Allocate the new argv array, with an extra element just in case + * the original arg list did not end with a NULL. + */ + nargv = (char**)calloc(*argcp+1, sizeof(char *)); + if (nargv == NULL) error("out of memory"); + oargv = *argvp; + *argvp = nargv; + + /* Copy the program name first */ + if (oargc-- < 0) error("argc<=0"); + *(nargv++) = *(oargv++); + + /* Then copy the environment args */ + for (p = env; nargc > 0; nargc--) { + p += strspn(p, SEPARATOR); /* skip separators */ + *(nargv++) = p; /* store start */ + while (*p++) ; /* skip over word */ + } + + /* Finally copy the old args and add a NULL (usual convention) */ + while (oargc--) *(nargv++) = *(oargv++); + *nargv = NULL; + return env; +} + +/* ======================================================================== + * Error handlers. + */ +void error(m) + char *m; +{ + fprintf(stderr, "\n%s: %s: %s\n", progname, ifname, m); + abort_gzip(); +} + +void warn(a, b) + char *a, *b; /* message strings juxtaposed in output */ +{ + WARN((stderr, "%s: %s: warning: %s%s\n", progname, ifname, a, b)); +} + +void read_error() +{ + fprintf(stderr, "\n%s: ", progname); + if (errno != 0) { + perror(ifname); + } else { + fprintf(stderr, "%s: unexpected end of file\n", ifname); + } + abort_gzip(); +} + +void write_error() +{ + fprintf(stderr, "\n%s: ", progname); + perror(ofname); + abort_gzip(); +} + +/* ======================================================================== + * Display compression ratio on the given stream on 6 characters. + */ +void display_ratio(num, den, file) + long num; + long den; + FILE *file; +{ + long ratio; /* 1000 times the compression ratio */ + char sign; + + if (den == 0) { + ratio = 0; /* no compression */ + } else if (den < 2147483L) { /* (2**31 -1)/1000 */ + ratio = 1000L*num/den; + } else { + ratio = num/(den/1000L); + } + if (ratio < 0) { + sign = '-'; + ratio = -ratio; + } else { + sign = ' '; + } + fprintf(file, "%c%2ld.%1ld%%", sign, ratio / 10L, ratio % 10L); +} + + +/* ======================================================================== + * Semi-safe malloc -- never returns NULL. + */ +voidp xmalloc (size) + unsigned size; +{ + voidp cp = (voidp)malloc (size); + + if (cp == NULL) error("out of memory"); + return cp; +} + +/* ======================================================================== + * Table of CRC-32's of all single-byte values (made by makecrc.c) + */ +ulg crc_32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; diff --git a/usr/gzip/zip.c b/usr/gzip/zip.c new file mode 100644 index 0000000..d0394c2 --- /dev/null +++ b/usr/gzip/zip.c @@ -0,0 +1,110 @@ +/* zip.c -- compress files to the gzip or pkzip format + * Copyright (C) 1992-1993 Jean-loup Gailly + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#ifdef RCSID +static char rcsid[] = "$Id: zip.c,v 1.1 2002/08/18 00:59:21 hpa Exp $"; +#endif + +#include <ctype.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> + +#include "tailor.h" +#include "gzip.h" + +local ulg crc; /* crc on uncompressed file data */ + +/* =========================================================================== + * Deflate in to out. + * IN assertions: the input and output buffers are cleared. + * The variables time_stamp and save_orig_name are initialized. + */ +int zip(in, out) + int in, out; /* input and output file descriptors */ +{ + uch flags = 0; /* general purpose bit flags */ + ush attr = 0; /* ascii/binary flag */ + ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */ + + ifd = in; + ofd = out; + outcnt = 0; + + /* Write the header to the gzip file. See algorithm.doc for the format */ + + method = DEFLATED; + put_byte(GZIP_MAGIC[0]); /* magic header */ + put_byte(GZIP_MAGIC[1]); + put_byte(DEFLATED); /* compression method */ + + if (save_orig_name) { + flags |= ORIG_NAME; + } + put_byte(flags); /* general flags */ + put_long(time_stamp); + + /* Write deflated file to zip file */ + crc = updcrc(0, 0); + + bi_init(out); + ct_init(&attr, &method); + lm_init(level, &deflate_flags); + + put_byte((uch)deflate_flags); /* extra flags */ + put_byte(OS_CODE); /* OS identifier */ + + if (save_orig_name) { + char *p = basename(ifname); /* Don't save the directory part. */ + do { + put_char(*p); + } while (*p++); + } + header_bytes = (long)outcnt; + + (void)deflate(); + +#if !defined(NO_SIZE_CHECK) && !defined(RECORD_IO) + /* Check input size (but not in VMS -- variable record lengths mess it up) + * and not on MSDOS -- diet in TSR mode reports an incorrect file size) + */ + if (ifile_size != -1L && isize != (ulg)ifile_size) { + Trace((stderr, " actual=%ld, read=%ld ", ifile_size, isize)); + fprintf(stderr, "%s: %s: file size changed while zipping\n", + progname, ifname); + } +#endif + + /* Write the crc and uncompressed size */ + put_long(crc); + put_long(isize); + header_bytes += 2*sizeof(long); + + flush_outbuf(); + return OK; +} + + +/* =========================================================================== + * Read a new buffer from the current input file, perform end-of-line + * translation, and update the crc and input file size. + * IN assertion: size >= 2 (for end-of-line translation) + */ +int file_read(buf, size) + char *buf; + unsigned size; +{ + unsigned len; + + Assert(insize == 0, "inbuf not empty"); + + len = read(ifd, buf, size); + if (len == (unsigned)(-1) || len == 0) return (int)len; + + crc = updcrc((uch*)buf, len); + isize += (ulg)len; + return (int)len; +} diff --git a/usr/include/Kbuild b/usr/include/Kbuild new file mode 100644 index 0000000..0032724 --- /dev/null +++ b/usr/include/Kbuild @@ -0,0 +1,11 @@ +always := asm + +$(obj)/asm: + @echo ' SYMLINK $@ -> include/asm-$(KLIBCASMARCH)' + $(Q)if [ '$(KLIBCKERNELSRC)/.' -ef '$(obj)/../..' ]; then \ + ln -fsn ../../include/asm-$(KLIBCASMARCH) $@; \ + else \ + ln -fsn $(KLIBCKERNELSRC)/include/asm-$(KLIBCASMARCH) $@; \ + fi + +clean-files := asm diff --git a/usr/include/alloca.h b/usr/include/alloca.h new file mode 100644 index 0000000..91ef4c0 --- /dev/null +++ b/usr/include/alloca.h @@ -0,0 +1,12 @@ +/* + * alloca.h + * + * Just call the builtin alloca() function + */ + +#ifndef _ALLOCA_H +#define _ALLOCA_H + +#define alloca(size) __builtin_alloca(size) + +#endif /* _ALLOCA_H */ diff --git a/usr/include/arch/alpha/klibc/archconfig.h b/usr/include/arch/alpha/klibc/archconfig.h new file mode 100644 index 0000000..6604211 --- /dev/null +++ b/usr/include/arch/alpha/klibc/archconfig.h @@ -0,0 +1,16 @@ +/* + * include/arch/alpha/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* We provide our own restorer that call rt_sigreturn() */ +#define _KLIBC_NEEDS_SA_SIGINFO 1 +#define _KLIBC_STATFS_F_TYPE_64 0 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/alpha/klibc/archsetjmp.h b/usr/include/arch/alpha/klibc/archsetjmp.h new file mode 100644 index 0000000..47638b3 --- /dev/null +++ b/usr/include/arch/alpha/klibc/archsetjmp.h @@ -0,0 +1,33 @@ +/* + * arch/alpha/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __s0; + unsigned long __s1; + unsigned long __s2; + unsigned long __s3; + unsigned long __s4; + unsigned long __s5; + unsigned long __fp; + unsigned long __ra; + unsigned long __gp; + unsigned long __sp; + + unsigned long __f2; + unsigned long __f3; + unsigned long __f4; + unsigned long __f5; + unsigned long __f6; + unsigned long __f7; + unsigned long __f8; + unsigned long __f9; +}; + +/* Must be an array so it will decay to a pointer when a function is called */ +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _KLIBC_ARCHSETJMP_H */ diff --git a/usr/include/arch/alpha/klibc/archsignal.h b/usr/include/arch/alpha/klibc/archsignal.h new file mode 100644 index 0000000..78be832 --- /dev/null +++ b/usr/include/arch/alpha/klibc/archsignal.h @@ -0,0 +1,88 @@ +/* + * arch/alpha/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +/* + * This is identical to <asm/signal.h>, *except* for _NSIG and struct + * sigaction, where it has the old definition and we need the new (RT) + * definition. + */ + +struct siginfo; + +#define NSIG 64 + +typedef unsigned long sigset_t; + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGEMT 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGBUS 10 +#define SIGSEGV 11 +#define SIGSYS 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGURG 16 +#define SIGSTOP 17 +#define SIGTSTP 18 +#define SIGCONT 19 +#define SIGCHLD 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGIO 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGINFO 29 +#define SIGUSR1 30 +#define SIGUSR2 31 + +#define SIGPOLL SIGIO +#define SIGPWR SIGINFO +#define SIGIOT SIGABRT + +#define SA_ONSTACK 0x00000001 +#define SA_RESTART 0x00000002 +#define SA_NOCLDSTOP 0x00000004 +#define SA_NODEFER 0x00000008 +#define SA_RESETHAND 0x00000010 +#define SA_NOCLDWAIT 0x00000020 +#define SA_SIGINFO 0x00000040 + +#define SA_ONESHOT SA_RESETHAND +#define SA_NOMASK SA_NODEFER + +#define MINSIGSTKSZ 4096 +#define SIGSTKSZ 16384 + +#define SIG_BLOCK 1 +#define SIG_UNBLOCK 2 +#define SIG_SETMASK 3 + +#include <asm-generic/signal-defs.h> + +struct sigaction { + union { + __sighandler_t sa_handler; + void (*sa_sigaction)(int, struct siginfo *, void *); + }; + unsigned long sa_flags; + sigset_t sa_mask; +}; + +#endif diff --git a/usr/include/arch/alpha/machine/asm.h b/usr/include/arch/alpha/machine/asm.h new file mode 100644 index 0000000..c2ae4ed --- /dev/null +++ b/usr/include/arch/alpha/machine/asm.h @@ -0,0 +1,44 @@ +/* + * machine/asm.h + */ + +#ifndef _MACHINE_ASM_H +#define _MACHINE_ASM_H + +/* Standard aliases for Alpha register names */ + +#define v0 $0 +#define t0 $1 +#define t1 $2 +#define t2 $3 +#define t3 $4 +#define t4 $5 +#define t5 $6 +#define t6 $7 +#define t7 $8 +#define s0 $9 +#define s1 $10 +#define s2 $11 +#define s3 $12 +#define s4 $13 +#define s5 $14 +#define fp $15 +#define a0 $16 +#define a1 $17 +#define a2 $18 +#define a3 $19 +#define a4 $20 +#define a5 $21 +#define t8 $22 +#define t9 $23 +#define t10 $24 +#define t11 $25 +#define ra $26 +#define t12 $27 /* t12 and pv are both used for $27 */ +#define pv $27 /* t12 and pv are both used for $27 */ +#define at $28 +#define gp $29 +#define sp $30 +#define zero $31 + +#endif /* _MACHINE_ASM_H */ diff --git a/usr/include/arch/arm/klibc/archconfig.h b/usr/include/arch/arm/klibc/archconfig.h new file mode 100644 index 0000000..36e9ab5 --- /dev/null +++ b/usr/include/arch/arm/klibc/archconfig.h @@ -0,0 +1,18 @@ +/* + * include/arch/arm/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* newer arm arch support bx instruction */ +#if (!defined(__ARM_ARCH_2__) && !defined(__ARM_ARCH_3__) \ + && !defined(__ARM_ARCH_3M__) && !defined(__ARM_ARCH_4__)) +# define _KLIBC_ARM_USE_BX 1 +#endif + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/arm/klibc/archsetjmp.h b/usr/include/arch/arm/klibc/archsetjmp.h new file mode 100644 index 0000000..88db8a1 --- /dev/null +++ b/usr/include/arch/arm/klibc/archsetjmp.h @@ -0,0 +1,14 @@ +/* + * arch/i386/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned int regs[10]; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/arm/klibc/archsignal.h b/usr/include/arch/arm/klibc/archsignal.h new file mode 100644 index 0000000..7189da7 --- /dev/null +++ b/usr/include/arch/arm/klibc/archsignal.h @@ -0,0 +1,122 @@ +/* + * arch/arm/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +/* The in-kernel headers for arm still have libc5 + crap in them. Reconsider using <asm/signal.h> + when/if it gets cleaned up; for now, duplicate + the definitions here. */ + +#define _NSIG 64 +#define _NSIG_BPW 32 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +/* +#define SIGLOST 29 +*/ +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +#define SIGSWI 32 + +/* + * SA_FLAGS values: + * + * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. + * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. + * SA_SIGINFO deliver the signal with SIGINFO structs + * SA_THIRTYTWO delivers the signal in 32-bit mode, even if the task + * is running in 26-bit. + * SA_ONSTACK allows alternate signal stacks (see sigaltstack(2)). + * SA_RESTART flag to get restarting signals (which were the default long ago) + * SA_NODEFER prevents the current signal from being masked in the handler. + * SA_RESETHAND clears the handler when the signal is delivered. + * + * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single + * Unix names RESETHAND and NODEFER respectively. + */ +#define SA_NOCLDSTOP 0x00000001 +#define SA_NOCLDWAIT 0x00000002 +#define SA_SIGINFO 0x00000004 +#define SA_THIRTYTWO 0x02000000 +#define SA_RESTORER 0x04000000 +#define SA_ONSTACK 0x08000000 +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND + + +/* + * sigaltstack controls + */ +#define SS_ONSTACK 1 +#define SS_DISABLE 2 + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 + +#include <asm-generic/signal-defs.h> + +/* This uses gcc anonymous union support... */ +struct siginfo; + +struct sigaction { + union { + __sighandler_t sa_handler; + void (*sa_sigaction)(int, struct siginfo *, void *); + }; + unsigned long sa_flags; + __sigrestore_t sa_restorer; + sigset_t sa_mask; +}; + +#endif diff --git a/usr/include/arch/arm/klibc/asmmacros.h b/usr/include/arch/arm/klibc/asmmacros.h new file mode 100644 index 0000000..a6810ce --- /dev/null +++ b/usr/include/arch/arm/klibc/asmmacros.h @@ -0,0 +1,16 @@ +/* + * usr/include/arch/arm/klibc/asmmacros.h + * + * Assembly macros used by ARM system call stubs + */ + +#ifndef _KLIBC_ASMMACROS_H +#define _KLIBC_ASMMACROS_H + +#if _KLIBC_ARM_USE_BX +# define BX(x) bx x +#else +# define BX(x) mov pc, x +#endif + +#endif /* _KLIBC_ASMMACROS_H */ diff --git a/usr/include/arch/arm64/klibc/archconfig.h b/usr/include/arch/arm64/klibc/archconfig.h new file mode 100644 index 0000000..d094c28 --- /dev/null +++ b/usr/include/arch/arm64/klibc/archconfig.h @@ -0,0 +1,15 @@ +/* + * include/arch/arm64/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +#define _KLIBC_NO_MMU 0 +#define _KLIBC_REAL_VFORK 1 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/arm64/klibc/archsetjmp.h b/usr/include/arch/arm64/klibc/archsetjmp.h new file mode 100644 index 0000000..edc3312 --- /dev/null +++ b/usr/include/arch/arm64/klibc/archsetjmp.h @@ -0,0 +1,22 @@ +/* + * arch/arm64/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +/* + * x19-x28 are callee saved, also save fp, lr, sp. + * d8-d15 are unused as we specify -mgeneral-regs-only as a build flag. + */ + +struct __jmp_buf { + uint64_t __x19, __x20, __x21, __x22; + uint64_t __x23, __x24, __x25, __x26; + uint64_t __x27, __x28, __x29, __x30; + uint64_t __sp; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/arm64/klibc/archsignal.h b/usr/include/arch/arm64/klibc/archsignal.h new file mode 100644 index 0000000..94e6bc8 --- /dev/null +++ b/usr/include/arch/arm64/klibc/archsignal.h @@ -0,0 +1,14 @@ +/* + * arch/arm64/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +/* No special stuff for this architecture */ + +#endif diff --git a/usr/include/arch/i386/klibc/archconfig.h b/usr/include/arch/i386/klibc/archconfig.h new file mode 100644 index 0000000..7d4d5cd --- /dev/null +++ b/usr/include/arch/i386/klibc/archconfig.h @@ -0,0 +1,15 @@ +/* + * include/arch/i386/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* We have klibc/archinit.h and __libc_archinit() */ +#define _KLIBC_HAS_ARCHINIT 1 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/i386/klibc/archinit.h b/usr/include/arch/i386/klibc/archinit.h new file mode 100644 index 0000000..8995ebf --- /dev/null +++ b/usr/include/arch/i386/klibc/archinit.h @@ -0,0 +1,18 @@ +/* + * arch/i386/include/klibc/archinit.h + * + * Architecture-specific libc initialization + */ + +#include <stdint.h> +#include <klibc/compiler.h> +#include <elf.h> +#include <sys/auxv.h> + +extern void (*__syscall_entry)(int, ...); + +static inline void __libc_archinit(void) +{ + if (__auxval[AT_SYSINFO]) + __syscall_entry = (void (*)(int, ...)) __auxval[AT_SYSINFO]; +} diff --git a/usr/include/arch/i386/klibc/archsetjmp.h b/usr/include/arch/i386/klibc/archsetjmp.h new file mode 100644 index 0000000..ea1ba3d --- /dev/null +++ b/usr/include/arch/i386/klibc/archsetjmp.h @@ -0,0 +1,19 @@ +/* + * arch/i386/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned int __ebx; + unsigned int __esp; + unsigned int __ebp; + unsigned int __esi; + unsigned int __edi; + unsigned int __eip; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/i386/klibc/archsignal.h b/usr/include/arch/i386/klibc/archsignal.h new file mode 100644 index 0000000..5b3379a --- /dev/null +++ b/usr/include/arch/i386/klibc/archsignal.h @@ -0,0 +1,75 @@ +/* + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#define _NSIG 64 +#define _NSIG_BPW 32 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +#define SA_RESTORER 0x04000000 + +#include <asm-generic/signal-defs.h> + +struct siginfo; + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + unsigned int sa_flags; + __sigrestore_t sa_restorer; + sigset_t sa_mask; /* mask last for extensibility */ +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif diff --git a/usr/include/arch/i386/klibc/diverr.h b/usr/include/arch/i386/klibc/diverr.h new file mode 100644 index 0000000..fa238ac --- /dev/null +++ b/usr/include/arch/i386/klibc/diverr.h @@ -0,0 +1,15 @@ +/* + * arch/i386/include/klibc/diverr.h + */ + +#ifndef _KLIBC_DIVERR_H +#define _KLIBC_DIVERR_H + +#include <signal.h> + +static __inline__ void __divide_error(void) +{ + asm volatile ("int $0"); +} + +#endif /* _KLIBC_DIVERR_H */ diff --git a/usr/include/arch/i386/sys/io.h b/usr/include/arch/i386/sys/io.h new file mode 100644 index 0000000..cf31b97 --- /dev/null +++ b/usr/include/arch/i386/sys/io.h @@ -0,0 +1,127 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * sys/io.h for the i386 architecture + * + * Basic I/O macros + */ + +#ifndef _SYS_IO_H +#define _SYS_IO_H 1 + +/* I/O-related system calls */ + +int iopl(int); +int ioperm(unsigned long, unsigned long, int); + +/* Basic I/O macros */ + +static __inline__ void outb(unsigned char __v, unsigned short __p) +{ + asm volatile ("outb %0,%1" : : "a" (__v), "dN" (__p)); +} + +static __inline__ void outw(unsigned short __v, unsigned short __p) +{ + asm volatile ("outw %0,%1" : : "a" (__v), "dN" (__p)); +} + +static __inline__ void outl(unsigned int __v, unsigned short __p) +{ + asm volatile ("outl %0,%1" : : "a" (__v), "dN" (__p)); +} + +static __inline__ unsigned char inb(unsigned short __p) +{ + unsigned char __v; + asm volatile ("inb %1,%0" : "=a" (__v) : "dN" (__p)); + return __v; +} + +static __inline__ unsigned short inw(unsigned short __p) +{ + unsigned short __v; + asm volatile ("inw %1,%0" : "=a" (__v) : "dN" (__p)); + return __v; +} + +static __inline__ unsigned int inl(unsigned short __p) +{ + unsigned int __v; + asm volatile ("inl %1,%0" : "=a" (__v) : "dN" (__p)); + return __v; +} + +/* String I/O macros */ + +static __inline__ void +outsb(unsigned short __p, const void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; outsb" + : "+S" (__d), "+c" (__n) + : "d" (__p)); +} + +static __inline__ void +outsw(unsigned short __p, const void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; outsw" + : "+S" (__d), "+c" (__n) + : "d" (__p)); +} + +static __inline__ void +outsl(unsigned short __p, const void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; outsl" + : "+S" (__d), "+c"(__n) + : "d" (__p)); +} + +static __inline__ void insb(unsigned short __p, void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; insb" + : "+D" (__d), "+c" (__n) + : "d" (__p)); +} + +static __inline__ void insw(unsigned short __p, void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; insw" + : "+D" (__d), "+c" (__n) + : "d" (__p)); +} + +static __inline__ void insl(unsigned short __p, void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; insl" + : "+D" (__d), "+c" (__n) + : "d" (__p)); +} + +#endif /* _SYS_IO_H */ diff --git a/usr/include/arch/i386/sys/vm86.h b/usr/include/arch/i386/sys/vm86.h new file mode 100644 index 0000000..c4651cd --- /dev/null +++ b/usr/include/arch/i386/sys/vm86.h @@ -0,0 +1,40 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * sys/vm86.h for i386 + */ + +#ifndef _SYS_VM86_H +#define _SYS_VM86_H 1 + +#include <asm/vm86.h> + +/* Actual system call */ +int vm86(struct vm86_struct *); + +#endif diff --git a/usr/include/arch/ia64/klibc/archconfig.h b/usr/include/arch/ia64/klibc/archconfig.h new file mode 100644 index 0000000..9bf3c07 --- /dev/null +++ b/usr/include/arch/ia64/klibc/archconfig.h @@ -0,0 +1,20 @@ +/* + * include/arch/ia64/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* IA64 doesn't have sys_fork, but it does have an MMU */ +#define _KLIBC_NO_MMU 0 +/* IA64 doesn't have sys_vfork, it has architecture-specific code */ +#define _KLIBC_REAL_VFORK 1 +/* Need to fix-up function pointers to function descriptor pointers + * in struct sigaction */ +#define _KLIBC_NEEDS_SIGACTION_FIXUP 1 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/ia64/klibc/archsetjmp.h b/usr/include/arch/ia64/klibc/archsetjmp.h new file mode 100644 index 0000000..43564ee --- /dev/null +++ b/usr/include/arch/ia64/klibc/archsetjmp.h @@ -0,0 +1,17 @@ +/* + * arch/ia64/include/klibc/archsetjmp.h + * + * Code borrowed from the FreeBSD kernel. + * + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +/* User code must not depend on the internal representation of jmp_buf. */ +#define _JBLEN 0x200 + +/* guaranteed 128-bit alignment! */ +typedef char jmp_buf[_JBLEN] __attribute__ ((aligned(16))); + +#endif diff --git a/usr/include/arch/ia64/klibc/archsignal.h b/usr/include/arch/ia64/klibc/archsignal.h new file mode 100644 index 0000000..7cfbf30 --- /dev/null +++ b/usr/include/arch/ia64/klibc/archsignal.h @@ -0,0 +1,32 @@ +/* + * arch/ia64/include/klibc/archsignal.h + * + * Architecture-specific signal definitions. + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +#define _NSIG 64 +#define _NSIG_BPW 64 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction) (int, struct siginfo *, void *); + } _u; + unsigned long sa_flags; + sigset_t sa_mask; +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif diff --git a/usr/include/arch/loongarch64/klibc/archconfig.h b/usr/include/arch/loongarch64/klibc/archconfig.h new file mode 100644 index 0000000..7a47b91 --- /dev/null +++ b/usr/include/arch/loongarch64/klibc/archconfig.h @@ -0,0 +1,15 @@ +/* + * include/arch/loongarch64/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* We have an MMU but no fork() syscall */ +#define _KLIBC_NO_MMU 0 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/loongarch64/klibc/archsetjmp.h b/usr/include/arch/loongarch64/klibc/archsetjmp.h new file mode 100644 index 0000000..939c0f5 --- /dev/null +++ b/usr/include/arch/loongarch64/klibc/archsetjmp.h @@ -0,0 +1,26 @@ +/* + * include/arch/loongarch64/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __ra; + unsigned long __sp; + unsigned long __r21; + unsigned long __fp; + unsigned long __s0; + unsigned long __s1; + unsigned long __s2; + unsigned long __s3; + unsigned long __s4; + unsigned long __s5; + unsigned long __s6; + unsigned long __s7; + unsigned long __s8; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/loongarch64/klibc/archsignal.h b/usr/include/arch/loongarch64/klibc/archsignal.h new file mode 100644 index 0000000..c22a5d7 --- /dev/null +++ b/usr/include/arch/loongarch64/klibc/archsignal.h @@ -0,0 +1,14 @@ +/* + * include/arch/loongarch64/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +/* No special stuff for this architecture */ + +#endif diff --git a/usr/include/arch/loongarch64/machine/asm.h b/usr/include/arch/loongarch64/machine/asm.h new file mode 100644 index 0000000..6fe5dba --- /dev/null +++ b/usr/include/arch/loongarch64/machine/asm.h @@ -0,0 +1,24 @@ +/* + * include/arch/loongarch64/machine/asm.h + */ + +#ifndef _MACHINE_ASM_H +#define _MACHINE_ASM_H + +/* + * ENTRY - declare entry point + */ +#define ENTRY(symbol) \ + .globl symbol; \ + .align 2; \ + .type symbol, @function; \ +symbol: + +/* + * END - mark end of function + */ +#define END(function) \ + .size function, . - function + + +#endif /* _MACHINE_ASM_H */ diff --git a/usr/include/arch/m68k/klibc/archconfig.h b/usr/include/arch/m68k/klibc/archconfig.h new file mode 100644 index 0000000..10ef62e --- /dev/null +++ b/usr/include/arch/m68k/klibc/archconfig.h @@ -0,0 +1,15 @@ +/* + * include/arch/m68k/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* On m68k, sys_mmap2 uses the current page size as the shift factor */ +#define _KLIBC_MMAP2_SHIFT __getpageshift() + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/m68k/klibc/archsetjmp.h b/usr/include/arch/m68k/klibc/archsetjmp.h new file mode 100644 index 0000000..e85c810 --- /dev/null +++ b/usr/include/arch/m68k/klibc/archsetjmp.h @@ -0,0 +1,26 @@ +/* + * usr/include/arch/m68k/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned int __d2; + unsigned int __d3; + unsigned int __d4; + unsigned int __d5; + unsigned int __d6; + unsigned int __d7; + unsigned int __a2; + unsigned int __a3; + unsigned int __a4; + unsigned int __a5; + unsigned int __fp; /* a6 */ + unsigned int __sp; /* a7 */ + unsigned int __retaddr; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _KLBIC_ARCHSETJMP_H */ diff --git a/usr/include/arch/m68k/klibc/archsignal.h b/usr/include/arch/m68k/klibc/archsignal.h new file mode 100644 index 0000000..6461346 --- /dev/null +++ b/usr/include/arch/m68k/klibc/archsignal.h @@ -0,0 +1,74 @@ +/* + * arch/m68k/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#define _NSIG 64 +#define _NSIG_BPW 32 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +#include <asm-generic/signal-defs.h> + +struct siginfo; + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + unsigned int sa_flags; + __sigrestore_t sa_restorer; + sigset_t sa_mask; /* mask last for extensibility */ +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif diff --git a/usr/include/arch/mips/klibc/archconfig.h b/usr/include/arch/mips/klibc/archconfig.h new file mode 100644 index 0000000..ff0afb5 --- /dev/null +++ b/usr/include/arch/mips/klibc/archconfig.h @@ -0,0 +1,21 @@ +/* + * include/arch/mips/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* MIPS has architecture-specific code for vfork() */ +#define _KLIBC_REAL_VFORK 1 + +/* MIPS defines it's own statfs */ +#define _KLIBC_STATFS_F_TYPE_32B 1 + +/* MIPS has nonstandard socket definitions */ +#define _KLIBC_HAS_ARCHSOCKET_H 1 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/mips/klibc/archfcntl.h b/usr/include/arch/mips/klibc/archfcntl.h new file mode 100644 index 0000000..586afb4 --- /dev/null +++ b/usr/include/arch/mips/klibc/archfcntl.h @@ -0,0 +1,107 @@ +/* + * arch/mips/include/klibc/archfcntl.h + * + * On MIPS, <asm/fcntl.h> isn't usable (compiling struct stat with + * the correct definitions doesn't "just work"), so we need to provide + * our own definitions. + */ + +#ifndef _KLIBC_ARCHFCNTL_H +#define _KLIBC_ARCHFCNTL_H + +#ifdef _ASM_FCNTL_H /* We were too late! */ +# error "<asm/fcntl.h> included before <klibc/archfcntl.h>" +#endif +#define _ASM_FCNTL_H /* Keep <asm/fcntl.h> from getting included */ + +#define O_ACCMODE 0x0003 +#define O_RDONLY 0x0000 +#define O_WRONLY 0x0001 +#define O_RDWR 0x0002 +#define O_APPEND 0x0008 +#define O_NONBLOCK 0x0080 +#define O_CREAT 0x0100 +#define O_TRUNC 0x0200 +#define O_EXCL 0x0400 +#define O_NOCTTY 0x0800 +#define FASYNC 0x1000 +#define O_LARGEFILE 0x2000 +#define O_SYNC 0x4010 +#define O_DIRECT 0x8000 +#define O_DIRECTORY 0x10000 +#define O_NOFOLLOW 0x20000 +#define O_NOATIME 0x40000 +#define O_CLOEXEC 0x80000 +#define O_PATH 0x200000 +#define O_TMPFILE 0x410000 + +#define O_NDELAY O_NONBLOCK + +#define F_DUPFD 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 +#define F_GETLK 14 +#define F_SETLK 6 +#define F_SETLKW 7 + +#define F_SETOWN 24 +#define F_GETOWN 23 +#define F_SETSIG 10 +#define F_GETSIG 11 + +#define F_GETLK64 33 +#define F_SETLK64 34 +#define F_SETLKW64 35 + +#define F_SETOWN_EX 15 +#define F_GETOWN_EX 16 + +#define F_GETOWNER_UIDS 17 + +#define F_OFD_GETLK 36 +#define F_OFD_SETLK 37 +#define F_OFD_SETLKW 38 + +#define F_OWNER_TID 0 +#define F_OWNER_PID 1 +#define F_OWNER_PGRP 2 + +struct f_owner_ex { + int type; + pid_t pid; +}; + +#define FD_CLOEXEC 1 + +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +#define F_EXLCK 4 +#define F_SHLCK 8 + +#define F_INPROGRESS 16 + +#define LOCK_SH 1 +#define LOCK_EX 2 +#define LOCK_NB 4 +#define LOCK_UN 8 + +#define LOCK_MAND 32 +#define LOCK_READ 64 +#define LOCK_WRITE 128 +#define LOCK_RW 192 + +typedef struct flock { + short l_type; + short l_whence; + loff_t l_start; + loff_t l_len; + pid_t l_pid; +} flock_t; + +#define F_LINUX_SPECIFIC_BASE 1024 + +#endif /* _KLIBC_ARCHFCNTL_H */ diff --git a/usr/include/arch/mips/klibc/archsetjmp.h b/usr/include/arch/mips/klibc/archsetjmp.h new file mode 100644 index 0000000..eeadffc --- /dev/null +++ b/usr/include/arch/mips/klibc/archsetjmp.h @@ -0,0 +1,25 @@ +/* + * arch/mips/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __s0; + unsigned long __s1; + unsigned long __s2; + unsigned long __s3; + unsigned long __s4; + unsigned long __s5; + unsigned long __s6; + unsigned long __s7; + unsigned long __gp; + unsigned long __sp; + unsigned long __s8; + unsigned long __ra; +} __attribute__ ((aligned(8))); + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _KLIBC_ARCHSETJMP_H */ diff --git a/usr/include/arch/mips/klibc/archsignal.h b/usr/include/arch/mips/klibc/archsignal.h new file mode 100644 index 0000000..b9ca756 --- /dev/null +++ b/usr/include/arch/mips/klibc/archsignal.h @@ -0,0 +1,14 @@ +/* + * arch/mips/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +/* No special stuff for this architecture */ + +#endif diff --git a/usr/include/arch/mips/klibc/archsocket.h b/usr/include/arch/mips/klibc/archsocket.h new file mode 100644 index 0000000..d6daf1b --- /dev/null +++ b/usr/include/arch/mips/klibc/archsocket.h @@ -0,0 +1,17 @@ +/* + * arch/mips/klibc/archsocket.h + */ + +#ifndef _KLIBC_ARCHSOCKET_H +#define _KLIBC_ARCHSOCKET_H + +#ifndef SOCK_STREAM +# define SOCK_DGRAM 1 +# define SOCK_STREAM 2 +# define SOCK_RAW 3 +# define SOCK_RDM 4 +# define SOCK_SEQPACKET 5 +# define SOCK_PACKET 10 +#endif + +#endif /* _KLIBC_ARCHSOCKET_H */ diff --git a/usr/include/arch/mips/machine/asm.h b/usr/include/arch/mips/machine/asm.h new file mode 100644 index 0000000..0b8cece --- /dev/null +++ b/usr/include/arch/mips/machine/asm.h @@ -0,0 +1,76 @@ +/* + * arch/mips/include/machine/asm.h + */ + +#ifndef _MACHINE_ASM_H +#define _MACHINE_ASM_H + +/* + * Symbolic register names for 32 bit ABI + */ + +#define zero $0 /* wired zero */ +#define AT $1 /* assembler temp - uppercase because of ".set at" */ +#define v0 $2 /* return value */ +#define v1 $3 +#define a0 $4 /* argument registers */ +#define a1 $5 +#define a2 $6 +#define a3 $7 +#define t0 $8 /* caller saved */ +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 +#define s0 $16 /* callee saved */ +#define s1 $17 +#define s2 $18 +#define s3 $19 +#define s4 $20 +#define s5 $21 +#define s6 $22 +#define s7 $23 +#define t8 $24 /* caller saved */ +#define t9 $25 +#define jp $25 /* PIC jump register */ +#define k0 $26 /* kernel scratch */ +#define k1 $27 +#define gp $28 /* global pointer */ +#define sp $29 /* stack pointer */ +#define fp $30 /* frame pointer */ +#define s8 $30 /* same like fp! */ +#define ra $31 /* return address */ + +/* + * LEAF - declare leaf routine + */ +#define LEAF(symbol) \ + .globl symbol; \ + .align 2; \ + .type symbol,@function; \ + .ent symbol,0; \ +symbol: .frame sp,0,ra + + +/* + * NESTED - declare nested routine entry point + */ +#define NESTED(symbol, framesize, rpc) \ + .globl symbol; \ + .align 2; \ + .type symbol,@function; \ + .ent symbol,0; \ +symbol: .frame sp, framesize, rpc + +/* + * END - mark end of function + */ +#define END(function) \ + .end function; \ + .size function,.-function + + +#endif /* _MACHINE_ASM_H */ diff --git a/usr/include/arch/mips/sgidefs.h b/usr/include/arch/mips/sgidefs.h new file mode 100644 index 0000000..fba8ae8 --- /dev/null +++ b/usr/include/arch/mips/sgidefs.h @@ -0,0 +1,20 @@ +/* + * arch/mips/include/sgidefs.h + */ + +/* Some ABI constants */ + +#ifndef _SGIDEFS_H +#define _SGIDEFS_H + +#define _MIPS_ISA_MIPS1 1 +#define _MIPS_ISA_MIPS2 2 +#define _MIPS_ISA_MIPS3 3 +#define _MIPS_ISA_MIPS4 4 +#define _MIPS_ISA_MIPS5 5 + +#define _MIPS_SIM_ABI32 1 +#define _MIPS_SIM_NABI32 2 +#define _MIPS_SIM_ABI64 3 + +#endif /* _SGIDEFS_H */ diff --git a/usr/include/arch/mips/spaces.h b/usr/include/arch/mips/spaces.h new file mode 100644 index 0000000..b5f530b --- /dev/null +++ b/usr/include/arch/mips/spaces.h @@ -0,0 +1 @@ +/* Included by <asm/page.h> but not actually needed */ diff --git a/usr/include/arch/mips64/klibc/archconfig.h b/usr/include/arch/mips64/klibc/archconfig.h new file mode 100644 index 0000000..fa31482 --- /dev/null +++ b/usr/include/arch/mips64/klibc/archconfig.h @@ -0,0 +1,20 @@ +/* + * include/arch/mips64/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* MIPS has nonstandard socket definitions */ +#define _KLIBC_HAS_ARCHSOCKET_H 1 + +#define _KLIBC_STATFS_F_TYPE_64 1 + +/* MIPS has architecture-specific code for vfork() */ +#define _KLIBC_REAL_VFORK 1 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/mips64/klibc/archsetjmp.h b/usr/include/arch/mips64/klibc/archsetjmp.h new file mode 100644 index 0000000..bfca777 --- /dev/null +++ b/usr/include/arch/mips64/klibc/archsetjmp.h @@ -0,0 +1,25 @@ +/* + * arch/mips64/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __s0; + unsigned long __s1; + unsigned long __s2; + unsigned long __s3; + unsigned long __s4; + unsigned long __s5; + unsigned long __s6; + unsigned long __s7; + unsigned long __gp; + unsigned long __sp; + unsigned long __s8; + unsigned long __ra; +} __attribute__ ((aligned(8))); + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _KLIBC_ARCHSETJMP_H */ diff --git a/usr/include/arch/mips64/klibc/archsignal.h b/usr/include/arch/mips64/klibc/archsignal.h new file mode 100644 index 0000000..f350af9 --- /dev/null +++ b/usr/include/arch/mips64/klibc/archsignal.h @@ -0,0 +1,14 @@ +/* + * arch/mips64/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +/* No special stuff for this architecture */ + +#endif diff --git a/usr/include/arch/mips64/klibc/archsocket.h b/usr/include/arch/mips64/klibc/archsocket.h new file mode 100644 index 0000000..6c3947d --- /dev/null +++ b/usr/include/arch/mips64/klibc/archsocket.h @@ -0,0 +1,17 @@ +/* + * arch/mips64/klibc/archsocket.h + */ + +#ifndef _KLIBC_ARCHSOCKET_H +#define _KLIBC_ARCHSOCKET_H + +#ifndef SOCK_STREAM +# define SOCK_DGRAM 1 +# define SOCK_STREAM 2 +# define SOCK_RAW 3 +# define SOCK_RDM 4 +# define SOCK_SEQPACKET 5 +# define SOCK_PACKET 10 +#endif + +#endif /* _KLIBC_ARCHSOCKET_H */ diff --git a/usr/include/arch/mips64/machine/asm.h b/usr/include/arch/mips64/machine/asm.h new file mode 100644 index 0000000..42dcaa4 --- /dev/null +++ b/usr/include/arch/mips64/machine/asm.h @@ -0,0 +1,82 @@ +/* + * arch/mips64/include/machine/asm.h + */ + +#ifndef _MACHINE_ASM_H +#define _MACHINE_ASM_H + +/* + * Symbolic register names for 64 bit ABI + */ + + +#define zero $0 /* wired zero */ +#define AT $at /* assembler temp - uppercase because of ".set at" */ +#define v0 $2 /* return value - caller saved */ +#define v1 $3 +#define a0 $4 /* argument registers */ +#define a1 $5 +#define a2 $6 +#define a3 $7 +#define a4 $8 /* arg reg 64 bit; caller saved in 32 bit */ +#define ta0 $8 +#define a5 $9 +#define ta1 $9 +#define a6 $10 +#define ta2 $10 +#define a7 $11 +#define ta3 $11 +#define t4 $12 /* caller saved */ +#define t5 $13 +#define t6 $14 +#define t7 $15 +#define s0 $16 /* callee saved */ +#define s1 $17 +#define s2 $18 +#define s3 $19 +#define s4 $20 +#define s5 $21 +#define s6 $22 +#define s7 $23 +#define t8 $24 /* caller saved */ +#define t9 $25 /* callee address for PIC/temp */ +#define jp $25 /* PIC jump register */ +#define k0 $26 /* kernel temporary */ +#define k1 $27 +#define gp $28 /* global pointer - caller saved for PIC */ +#define sp $29 /* stack pointer */ +#define fp $30 /* frame pointer */ +#define s8 $30 /* callee saved */ +#define ra $31 /* return address */ + + +/* + * LEAF - declare leaf routine + */ +#define LEAF(symbol) \ + .globl symbol; \ + .align 2; \ + .type symbol,@function; \ + .ent symbol,0; \ +symbol: .frame sp,0,ra + + +/* + * NESTED - declare nested routine entry point + */ +#define NESTED(symbol, framesize, rpc) \ + .globl symbol; \ + .align 2; \ + .type symbol,@function; \ + .ent symbol,0; \ +symbol: .frame sp, framesize, rpc + +/* + * END - mark end of function + */ +#define END(function) \ + .end function; \ + .size function,.-function + + +#endif /* _MACHINE_ASM_H */ diff --git a/usr/include/arch/parisc/klibc/archconfig.h b/usr/include/arch/parisc/klibc/archconfig.h new file mode 100644 index 0000000..f8ba9e2 --- /dev/null +++ b/usr/include/arch/parisc/klibc/archconfig.h @@ -0,0 +1,14 @@ +/* + * include/arch/parisc/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* All defaults */ + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/parisc/klibc/archsetjmp.h b/usr/include/arch/parisc/klibc/archsetjmp.h new file mode 100644 index 0000000..05e943e --- /dev/null +++ b/usr/include/arch/parisc/klibc/archsetjmp.h @@ -0,0 +1,14 @@ +/* + * arch/parisc/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + double regs[21]; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/parisc/klibc/archsignal.h b/usr/include/arch/parisc/klibc/archsignal.h new file mode 100644 index 0000000..256aeea --- /dev/null +++ b/usr/include/arch/parisc/klibc/archsignal.h @@ -0,0 +1,25 @@ +/* + * arch/parisc/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +#define _NSIG 64 +#define _NSIG_SZ (_NSIG / LONG_BIT) + +typedef struct { + unsigned long sig[_NSIG_SZ]; +} sigset_t; + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + sigset_t sa_mask; +}; + +#endif diff --git a/usr/include/arch/ppc/klibc/archconfig.h b/usr/include/arch/ppc/klibc/archconfig.h new file mode 100644 index 0000000..584af21 --- /dev/null +++ b/usr/include/arch/ppc/klibc/archconfig.h @@ -0,0 +1,12 @@ +/* + * include/arch/ppc/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/ppc/klibc/archsetjmp.h b/usr/include/arch/ppc/klibc/archsetjmp.h new file mode 100644 index 0000000..4be9ed6 --- /dev/null +++ b/usr/include/arch/ppc/klibc/archsetjmp.h @@ -0,0 +1,36 @@ +/* + * arch/ppc/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __r2; + unsigned long __sp; + unsigned long __lr; + unsigned long __cr; + unsigned long __r13; + unsigned long __r14; + unsigned long __r15; + unsigned long __r16; + unsigned long __r17; + unsigned long __r18; + unsigned long __r19; + unsigned long __r20; + unsigned long __r21; + unsigned long __r22; + unsigned long __r23; + unsigned long __r24; + unsigned long __r25; + unsigned long __r26; + unsigned long __r27; + unsigned long __r28; + unsigned long __r29; + unsigned long __r30; + unsigned long __r31; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/ppc/klibc/archsignal.h b/usr/include/arch/ppc/klibc/archsignal.h new file mode 100644 index 0000000..9c3ac92 --- /dev/null +++ b/usr/include/arch/ppc/klibc/archsignal.h @@ -0,0 +1,14 @@ +/* + * arch/ppc/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +/* No special stuff for this architecture */ + +#endif diff --git a/usr/include/arch/ppc64/klibc/archconfig.h b/usr/include/arch/ppc64/klibc/archconfig.h new file mode 100644 index 0000000..61b61f4 --- /dev/null +++ b/usr/include/arch/ppc64/klibc/archconfig.h @@ -0,0 +1,10 @@ +/* + * include/arch/ppc64/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in this file. + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/ppc64/klibc/archsetjmp.h b/usr/include/arch/ppc64/klibc/archsetjmp.h new file mode 100644 index 0000000..d227728 --- /dev/null +++ b/usr/include/arch/ppc64/klibc/archsetjmp.h @@ -0,0 +1,36 @@ +/* + * arch/ppc64/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __r2; + unsigned long __sp; + unsigned long __lr; + unsigned long __cr; + unsigned long __r13; + unsigned long __r14; + unsigned long __r15; + unsigned long __r16; + unsigned long __r17; + unsigned long __r18; + unsigned long __r19; + unsigned long __r20; + unsigned long __r21; + unsigned long __r22; + unsigned long __r23; + unsigned long __r24; + unsigned long __r25; + unsigned long __r26; + unsigned long __r27; + unsigned long __r28; + unsigned long __r29; + unsigned long __r30; + unsigned long __r31; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/ppc64/klibc/archsignal.h b/usr/include/arch/ppc64/klibc/archsignal.h new file mode 100644 index 0000000..2c4cef0 --- /dev/null +++ b/usr/include/arch/ppc64/klibc/archsignal.h @@ -0,0 +1,14 @@ +/* + * arch/ppc64/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +/* No special stuff for this architecture */ + +#endif diff --git a/usr/include/arch/riscv64/klibc/archconfig.h b/usr/include/arch/riscv64/klibc/archconfig.h new file mode 100644 index 0000000..e85a69c --- /dev/null +++ b/usr/include/arch/riscv64/klibc/archconfig.h @@ -0,0 +1,15 @@ +/* + * include/arch/riscv64/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* We have an MMU but no fork() syscall */ +#define _KLIBC_NO_MMU 0 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/riscv64/klibc/archsetjmp.h b/usr/include/arch/riscv64/klibc/archsetjmp.h new file mode 100644 index 0000000..97d6b6b --- /dev/null +++ b/usr/include/arch/riscv64/klibc/archsetjmp.h @@ -0,0 +1,27 @@ +/* + * arch/riscv64/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __pc; + unsigned long __s0; + unsigned long __s1; + unsigned long __s2; + unsigned long __s3; + unsigned long __s4; + unsigned long __s5; + unsigned long __s6; + unsigned long __s7; + unsigned long __s8; + unsigned long __s9; + unsigned long __s10; + unsigned long __s11; + unsigned long __sp; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/riscv64/klibc/archsignal.h b/usr/include/arch/riscv64/klibc/archsignal.h new file mode 100644 index 0000000..560a951 --- /dev/null +++ b/usr/include/arch/riscv64/klibc/archsignal.h @@ -0,0 +1,14 @@ +/* + * arch/riscv/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +/* No special stuff for this architecture */ + +#endif diff --git a/usr/include/arch/riscv64/machine/asm.h b/usr/include/arch/riscv64/machine/asm.h new file mode 100644 index 0000000..9effc93 --- /dev/null +++ b/usr/include/arch/riscv64/machine/asm.h @@ -0,0 +1,26 @@ +/* + * arch/riscv64/include/machine/asm.h + * + * Mostly cribbed from mips. + */ + +#ifndef _MACHINE_ASM_H +#define _MACHINE_ASM_H + +/* + * ENTRY - declare entry point + */ +#define ENTRY(symbol) \ + .globl symbol; \ + .align 2; \ + .type symbol, @function; \ +symbol: + +/* + * END - mark end of function + */ +#define END(function) \ + .size function, . - function + + +#endif /* _MACHINE_ASM_H */ diff --git a/usr/include/arch/s390/klibc/archconfig.h b/usr/include/arch/s390/klibc/archconfig.h new file mode 100644 index 0000000..b08bbb3 --- /dev/null +++ b/usr/include/arch/s390/klibc/archconfig.h @@ -0,0 +1,20 @@ +/* + * include/arch/s390/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* Both s390 and s390x use the "32-bit" version of this structure */ +#define _KLIBC_STATFS_F_TYPE_64 0 + +/* So that we can avoid stack trampolines */ +#define _KLIBC_NEEDS_SA_RESTORER 1 +/* Our restorer will call rt_sigreturn() */ +#define _KLIBC_NEEDS_SA_SIGINFO 1 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/s390/klibc/archsetjmp.h b/usr/include/arch/s390/klibc/archsetjmp.h new file mode 100644 index 0000000..1167c8b --- /dev/null +++ b/usr/include/arch/s390/klibc/archsetjmp.h @@ -0,0 +1,26 @@ +/* + * arch/s390/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +#ifndef __s390x__ + +struct __jmp_buf { + uint32_t __gregs[10]; /* general registers r6-r15 */ + uint64_t __fpregs[2]; /* fp registers f4 and f6 */ +}; + +#else /* __s390x__ */ + +struct __jmp_buf { + uint64_t __gregs[10]; /* general registers r6-r15 */ + uint64_t __fpregs[8]; /* fp registers f8-f15 */ +}; + +#endif /* __s390x__ */ + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/s390/klibc/archsignal.h b/usr/include/arch/s390/klibc/archsignal.h new file mode 100644 index 0000000..31993a4 --- /dev/null +++ b/usr/include/arch/s390/klibc/archsignal.h @@ -0,0 +1,76 @@ +/* + * arch/s390/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#define _NSIG 64 +#define _NSIG_BPW __BITS_PER_LONG +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +#define SA_RESTORER 0x04000000 + +#include <asm-generic/signal-defs.h> + +struct siginfo; + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif diff --git a/usr/include/arch/sh/klibc/archconfig.h b/usr/include/arch/sh/klibc/archconfig.h new file mode 100644 index 0000000..1f1f4a7 --- /dev/null +++ b/usr/include/arch/sh/klibc/archconfig.h @@ -0,0 +1,12 @@ +/* + * include/arch/sh/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/sh/klibc/archsetjmp.h b/usr/include/arch/sh/klibc/archsetjmp.h new file mode 100644 index 0000000..bb97167 --- /dev/null +++ b/usr/include/arch/sh/klibc/archsetjmp.h @@ -0,0 +1,22 @@ +/* + * arch/sh/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __r8; + unsigned long __r9; + unsigned long __r10; + unsigned long __r11; + unsigned long __r12; + unsigned long __r13; + unsigned long __r14; + unsigned long __r15; + unsigned long __pr; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _KLIBC_ARCHSETJMP_H */ diff --git a/usr/include/arch/sh/klibc/archsignal.h b/usr/include/arch/sh/klibc/archsignal.h new file mode 100644 index 0000000..8e48e51 --- /dev/null +++ b/usr/include/arch/sh/klibc/archsignal.h @@ -0,0 +1,14 @@ +/* + * arch/sh/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#include <asm/signal.h> +/* No special stuff for this architecture */ + +#endif diff --git a/usr/include/arch/sparc/klibc/archconfig.h b/usr/include/arch/sparc/klibc/archconfig.h new file mode 100644 index 0000000..bdc8b1e --- /dev/null +++ b/usr/include/arch/sparc/klibc/archconfig.h @@ -0,0 +1,19 @@ +/* + * include/arch/sparc/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +#define _KLIBC_SYS_SOCKETCALL 1 /* Use sys_socketcall unconditionally */ + +/* So that we can avoid stack trampolines */ +#define _KLIBC_NEEDS_SA_RESTORER 1 +/* Our restorer will call rt_sigreturn() */ +#define _KLIBC_NEEDS_SA_SIGINFO 1 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/sparc/klibc/archsetjmp.h b/usr/include/arch/sparc/klibc/archsetjmp.h new file mode 100644 index 0000000..9b4d6a2 --- /dev/null +++ b/usr/include/arch/sparc/klibc/archsetjmp.h @@ -0,0 +1,16 @@ +/* + * arch/sparc/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __sp; + unsigned long __fp; + unsigned long __pc; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/sparc/klibc/archsignal.h b/usr/include/arch/sparc/klibc/archsignal.h new file mode 100644 index 0000000..b0de544 --- /dev/null +++ b/usr/include/arch/sparc/klibc/archsignal.h @@ -0,0 +1,17 @@ +/* + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#define __WANT_POSIX1B_SIGNALS__ + +#include <linux/signal.h> + +/* Not actually used by the kernel... */ +#define SA_RESTORER 0x80000000 + +#endif diff --git a/usr/include/arch/sparc/machine/asm.h b/usr/include/arch/sparc/machine/asm.h new file mode 100644 index 0000000..fd9ef1e --- /dev/null +++ b/usr/include/arch/sparc/machine/asm.h @@ -0,0 +1,202 @@ +/* $NetBSD: asm.h,v 1.14 2002/07/20 08:37:30 mrg Exp $ */ + +/* + * Copyright (c) 1994 Allen Briggs + * All rights reserved. + * + * Gleaned from locore.s and sun3 asm.h which had the following copyrights: + * locore.s: + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1990 The Regents of the University of California. + * sun3/include/asm.h: + * Copyright (c) 1993 Adam Glass + * Copyright (c) 1990 The Regents of the University of California. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef _ASM_H_ +#define _ASM_H_ + +/* Pull in CCFSZ, CC64FSZ, and BIAS from frame.h */ +#ifndef _LOCORE +#define _LOCORE +#endif +#include <machine/frame.h> + +#ifdef __ELF__ +#define _C_LABEL(name) name +#else +#ifdef __STDC__ +#define _C_LABEL(name) _ ## name +#else +#define _C_LABEL(name) _/**/name +#endif +#endif +#define _ASM_LABEL(name) name + +#ifdef __PIC__ +/* + * PIC_PROLOGUE() is akin to the compiler generated function prologue for + * PIC code. It leaves the address of the Global Offset Table in DEST, + * clobbering register TMP in the process. + * + * We can use two code sequences. We can read the %pc or use the call + * instruction that saves the pc in %o7. Call requires the branch unit and + * IEU1, and clobbers %o7 which needs to be restored. This instruction + * sequence takes about 4 cycles due to instruction interdependence. Reading + * the pc takes 4 cycles to dispatch and is always dispatched alone. That + * sequence takes 7 cycles. + */ +#ifdef __arch64__ +#define PIC_PROLOGUE(dest,tmp) \ + mov %o7, tmp; \ + sethi %hi(_GLOBAL_OFFSET_TABLE_-4),dest; \ + call 0f; \ + or dest,%lo(_GLOBAL_OFFSET_TABLE_+4),dest; \ +0: \ + add dest,%o7,dest; \ + mov tmp, %o7 +#define SET(var,base,dest) \ + sethi %gdop_hix22(var), dest; \ + xor dest, %gdop_lox10(var), dest; \ + ldx [base + dest], dest, %gdop(var) +#else +#define PIC_PROLOGUE(dest,tmp) \ + mov %o7,tmp; 3: call 4f; nop; 4: \ + sethi %hi(_C_LABEL(_GLOBAL_OFFSET_TABLE_)-(3b-.)),dest; \ + or dest,%lo(_C_LABEL(_GLOBAL_OFFSET_TABLE_)-(3b-.)),dest; \ + add dest,%o7,dest; mov tmp,%o7 +#define SET(var,base,dest) \ + sethi %gdop_hix22(var), dest; \ + xor dest, %gdop_lox10(var), dest; \ + ld [base + dest], dest, %gdop(var) +#endif + +/* + * PICCY_SET() does the equivalent of a `set var, %dest' instruction in + * a PIC-like way, but without involving the Global Offset Table. This + * only works for VARs defined in the same file *and* in the text segment. + */ +#ifdef __arch64__ +#define PICCY_SET(var,dest,tmp) \ + 3: rd %pc, tmp; add tmp,(var-3b),dest +#else +#define PICCY_SET(var,dest,tmp) \ + mov %o7,tmp; 3: call 4f; nop; 4: \ + add %o7,(var-3b),dest; mov tmp,%o7 +#endif +#else +#define PIC_PROLOGUE(dest,tmp) +#define SET(var,base,dest) \ + sethi %hi(var), dest; \ + or dest, %lo(var), dest +#define PICCY_SET(var,dest,tmp) SET(var,tmp,dest) +#endif + +#define FTYPE(x) .type x,@function +#define OTYPE(x) .type x,@object + +#define _ENTRY(name) \ + .align 4; .globl name; .proc 1; FTYPE(name); name: + +#ifdef GPROF +/* see _MCOUNT_ENTRY in profile.h */ +#ifdef __ELF__ +#ifdef __arch64__ +#define _PROF_PROLOGUE \ + .data; .align 8; 1: .uaword 0; .uaword 0; \ + .text; save %sp,-CC64FSZ,%sp; sethi %hi(1b),%o0; call _mcount; \ + or %o0,%lo(1b),%o0; restore +#else +#define _PROF_PROLOGUE \ + .data; .align 4; 1: .long 0; \ + .text; save %sp,-96,%sp; sethi %hi(1b),%o0; call _mcount; \ + or %o0,%lo(1b),%o0; restore +#endif +#else +#ifdef __arch64__ +#define _PROF_PROLOGUE \ + .data; .align 8; 1: .uaword 0; .uaword 0; \ + .text; save %sp,-CC64FSZ,%sp; sethi %hi(1b),%o0; call mcount; \ + or %o0,%lo(1b),%o0; restore +#else +#define _PROF_PROLOGUE \ + .data; .align 4; 1: .long 0; \ + .text; save %sp,-96,%sp; sethi %hi(1b),%o0; call mcount; \ + or %o0,%lo(1b),%o0; restore +#endif +#endif +#else +#define _PROF_PROLOGUE +#endif + +#define ENTRY(name) _ENTRY(_C_LABEL(name)); _PROF_PROLOGUE +#define ENTRY_NOPROFILE(name) _ENTRY(_C_LABEL(name)) +#define ASENTRY(name) _ENTRY(_ASM_LABEL(name)); _PROF_PROLOGUE +#define FUNC(name) ASENTRY(name) +#define RODATA(name) .align 4; .text; .globl _C_LABEL(name); \ + OTYPE(_C_LABEL(name)); _C_LABEL(name): + +#define ASMSTR .asciz + +#define RCSID(name) .asciz name + +#ifdef __ELF__ +#define WEAK_ALIAS(alias,sym) \ + .weak alias; \ + alias = sym +#endif + +/* + * WARN_REFERENCES: create a warning if the specified symbol is referenced. + */ +#ifdef __ELF__ +#ifdef __STDC__ +#define WARN_REFERENCES(_sym,_msg) \ + .section .gnu.warning. ## _sym ; .ascii _msg ; .text +#else +#define WARN_REFERENCES(_sym,_msg) \ + .section .gnu.warning./**/_sym ; .ascii _msg ; .text +#endif /* __STDC__ */ +#else +#ifdef __STDC__ +#define __STRING(x) #x +#define WARN_REFERENCES(sym,msg) \ + .stabs msg ## ,30,0,0,0 ; \ + .stabs __STRING(_ ## sym) ## ,1,0,0,0 +#else +#define __STRING(x) "x" +#define WARN_REFERENCES(sym,msg) \ + .stabs msg,30,0,0,0 ; \ + .stabs __STRING(_/**/sym),1,0,0,0 +#endif /* __STDC__ */ +#endif /* __ELF__ */ + +#endif /* _ASM_H_ */ diff --git a/usr/include/arch/sparc/machine/frame.h b/usr/include/arch/sparc/machine/frame.h new file mode 100644 index 0000000..6fb9c45 --- /dev/null +++ b/usr/include/arch/sparc/machine/frame.h @@ -0,0 +1,146 @@ +/* $NetBSD: frame.h,v 1.4 2001/12/04 00:05:05 darrenr Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)frame.h 8.1 (Berkeley) 6/11/93 + */ + +#ifndef _MACHINE_FRAME_H +#define _MACHINE_FRAME_H + +#ifdef __ASSEMBLY__ +# define _LOCORE +#endif + +#ifndef _LOCORE +# include <stdint.h> +#endif + +/* + * Sparc stack frame format. + * + * Note that the contents of each stack frame may be held only in + * machine register windows. In order to get an accurate picture + * of the frame, you must first force the kernel to write any such + * windows to the stack. + */ +#ifndef _LOCORE +#ifndef SUN4U +struct frame { + int32_t fr_local[8]; /* space to save locals (%l0..%l7) */ + int32_t fr_arg[6]; /* space to save arguments (%i0..%i5) */ + struct frame *fr_fp; /* space to save frame pointer (%i6) */ + int32_t fr_pc; /* space to save return pc (%i7) */ + /* + * SunOS reserves another 8 words here; this is pointless + * but we do it for compatibility. + */ + int32_t fr_xxx; /* `structure return pointer' (unused) */ + int32_t fr_argd[6]; /* `arg dump area' (lunacy) */ + int32_t fr_argx[1]; /* arg extension (args 7..n; variable size) */ +}; +#else +struct frame32 { + int32_t fr_local[8]; /* space to save locals (%l0..%l7) */ + int32_t fr_arg[6]; /* space to save arguments (%i0..%i5) */ + uint32_t fr_fp; /* space to save frame pointer (%i6) */ + uint32_t fr_pc; /* space to save return pc (%i7) */ + /* + * SunOS reserves another 8 words here; this is pointless + * but we do it for compatibility. + */ + int32_t fr_xxx; /* `structure return pointer' (unused) */ + int32_t fr_argd[6]; /* `arg dump area' (lunacy) */ + int32_t fr_argx[1]; /* arg extension (args 7..n; variable size) */ +}; +#endif +#endif + +/* + * CCFSZ (C Compiler Frame SiZe) is the size of a stack frame required if + * a function is to call C code. It should be just 64, but Sun defined + * their frame with space to hold arguments 0 through 5 (plus some junk), + * and varargs routines (such as kprintf) demand this, and gcc uses this + * area at times anyway. + */ +#define CCFSZ 96 + +/* + * Sparc v9 stack frame format. + * + * Note that the contents of each stack frame may be held only in + * machine register windows. In order to get an accurate picture + * of the frame, you must first force the kernel to write any such + * windows to the stack. + * + * V9 frames have an odd bias, so you can tall a v9 frame from + * a v8 frame by testing the stack pointer's lsb. + */ +#if !defined(_LOCORE) && !defined(_LIBC) +struct frame64 { + int64_t fr_local[8]; /* space to save locals (%l0..%l7) */ + int64_t fr_arg[6]; /* space to save arguments (%i0..%i5) */ + uint64_t fr_fp; /* space to save frame pointer (%i6) */ + uint64_t fr_pc; /* space to save return pc (%i7) */ + /* + * SVR4 reserves a bunch of extra stuff. + */ + int64_t fr_argd[6]; /* `register save area' (lunacy) */ + int64_t fr_argx[0]; /* arg extension (args 7..n; variable size) */ +}; + +#define v9next_frame(f) ((struct frame64*)(f->fr_fp+BIAS)) +#endif + +/* + * CC64FSZ (C Compiler 64-bit Frame SiZe) is the size of a stack frame used + * by the compiler in 64-bit mode. It is (16)*8; space for 8 ins, 8 outs. + */ +#define CC64FSZ 176 + +/* + * v9 stacks all have a bias of 2047 added to the %sp and %fp, so you can easily + * detect it by testing the register for an odd value. Why 2K-1 I don't know. + */ +#define BIAS (2048-1) + +#endif /* _MACHINE_FRAME_H */ diff --git a/usr/include/arch/sparc/machine/trap.h b/usr/include/arch/sparc/machine/trap.h new file mode 100644 index 0000000..5c378c5 --- /dev/null +++ b/usr/include/arch/sparc/machine/trap.h @@ -0,0 +1,140 @@ +/* $NetBSD: trap.h,v 1.11 1999/01/20 00:15:08 pk Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)trap.h 8.1 (Berkeley) 6/11/93 + */ +/* + * Sun4m support by Aaron Brown, Harvard University. + * Changes Copyright (c) 1995 The President and Fellows of Harvard College. + * All rights reserved. + */ + +#ifndef _MACHINE_TRAP_H +#define _MACHINE_TRAP_H + +/* trap vec (pri) description */ +#define T_RESET 0x00 /* (1) not actually vectored; jumps to 0 */ +#define T_TEXTFAULT 0x01 /* (2) address fault during instr fetch */ +#define T_ILLINST 0x02 /* (3) illegal instruction */ +#define T_PRIVINST 0x03 /* (4) privileged instruction */ +#define T_FPDISABLED 0x04 /* (5) fp instr while fp disabled */ +#define T_WINOF 0x05 /* (6) register window overflow */ +#define T_WINUF 0x06 /* (7) register window underflow */ +#define T_ALIGN 0x07 /* (8) address not properly aligned */ +#define T_FPE 0x08 /* (9) floating point exception */ +#define T_DATAFAULT 0x09 /* (10) address fault during data fetch */ +#define T_TAGOF 0x0a /* (11) tag overflow */ +/* 0x0b unused */ +/* 0x0c unused */ +/* 0x0d unused */ +/* 0x0e unused */ +/* 0x0f unused */ +/* 0x10 unused */ +#define T_L1INT 0x11 /* (27) level 1 interrupt */ +#define T_L2INT 0x12 /* (26) level 2 interrupt */ +#define T_L3INT 0x13 /* (25) level 3 interrupt */ +#define T_L4INT 0x14 /* (24) level 4 interrupt */ +#define T_L5INT 0x15 /* (23) level 5 interrupt */ +#define T_L6INT 0x16 /* (22) level 6 interrupt */ +#define T_L7INT 0x17 /* (21) level 7 interrupt */ +#define T_L8INT 0x18 /* (20) level 8 interrupt */ +#define T_L9INT 0x19 /* (19) level 9 interrupt */ +#define T_L10INT 0x1a /* (18) level 10 interrupt */ +#define T_L11INT 0x1b /* (17) level 11 interrupt */ +#define T_L12INT 0x1c /* (16) level 12 interrupt */ +#define T_L13INT 0x1d /* (15) level 13 interrupt */ +#define T_L14INT 0x1e /* (14) level 14 interrupt */ +#define T_L15INT 0x1f /* (13) level 15 interrupt */ +/* 0x20 unused */ +/* through 0x23 unused */ +#define T_CPDISABLED 0x24 /* (5) coprocessor instr while disabled */ +#define T_UNIMPLFLUSH 0x25 /* Unimplemented FLUSH */ +/* through 0x27 unused */ +#define T_CPEXCEPTION 0x28 /* (9) coprocessor exception */ +/* 0x29 unused */ +#define T_IDIV0 0x2a /* divide by zero (from hw [su]div instr) */ +#define T_STOREBUFFAULT 0x2b /* SuperSPARC: Store buffer copy-back fault */ +/* 0x2c unused */ +/* through 0x7f unused */ + +/* beginning of `user' vectors (from trap instructions) - all priority 12 */ +#define T_SUN_SYSCALL 0x80 /* system call */ +#define T_BREAKPOINT 0x81 /* breakpoint `instruction' */ +#define T_DIV0 0x82 /* division routine was handed 0 */ +#define T_FLUSHWIN 0x83 /* flush windows */ +#define T_CLEANWIN 0x84 /* provide clean windows */ +#define T_RANGECHECK 0x85 /* ? */ +#define T_FIXALIGN 0x86 /* fix up unaligned accesses */ +#define T_INTOF 0x87 /* integer overflow ? */ +#define T_SVR4_SYSCALL 0x88 /* SVR4 system call */ +#define T_BSD_SYSCALL 0x89 /* BSD system call */ +#define T_KGDB_EXEC 0x8a /* for kernel gdb */ + +/* 0x8b..0xff are currently unallocated, except the following */ +#define T_SVR4_GETCC 0xa0 +#define T_SVR4_SETCC 0xa1 +#define T_SVR4_GETPSR 0xa2 +#define T_SVR4_SETPSR 0xa3 +#define T_SVR4_GETHRTIME 0xa4 +#define T_SVR4_GETHRVTIME 0xa5 +#define T_SVR4_GETHRESTIME 0xa7 + +#ifdef _KERNEL /* pseudo traps for locore.s */ +#define T_RWRET -1 /* need first user window for trap return */ +#define T_AST -2 /* no-op, just needed reschedule or profile */ +#endif + +/* flags to system call (flags in %g1 along with syscall number) */ +#define SYSCALL_G2RFLAG 0x400 /* on success, return to %g2 rather than npc */ +#define SYSCALL_G7RFLAG 0x800 /* use %g7 as above (deprecated) */ + +/* + * `software trap' macros to keep people happy (sparc v8 manual says not + * to set the upper bits). + */ +#define ST_BREAKPOINT (T_BREAKPOINT & 0x7f) +#define ST_DIV0 (T_DIV0 & 0x7f) +#define ST_FLUSHWIN (T_FLUSHWIN & 0x7f) +#define ST_SYSCALL (T_SUN_SYSCALL & 0x7f) + +#endif /* _MACHINE_TRAP_H_ */ diff --git a/usr/include/arch/sparc64/klibc/archconfig.h b/usr/include/arch/sparc64/klibc/archconfig.h new file mode 100644 index 0000000..df5c806 --- /dev/null +++ b/usr/include/arch/sparc64/klibc/archconfig.h @@ -0,0 +1,15 @@ +/* + * include/arch/sparc64/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +#define _KLIBC_NEEDS_SA_RESTORER 1 /* Need a restorer function */ +#define _KLIBC_SYS_SOCKETCALL 1 /* Use sys_socketcall unconditionally */ + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/sparc64/klibc/archsetjmp.h b/usr/include/arch/sparc64/klibc/archsetjmp.h new file mode 100644 index 0000000..9e825bd --- /dev/null +++ b/usr/include/arch/sparc64/klibc/archsetjmp.h @@ -0,0 +1,16 @@ +/* + * arch/sparc64/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __sp; + unsigned long __fp; + unsigned long __pc; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/sparc64/klibc/archsignal.h b/usr/include/arch/sparc64/klibc/archsignal.h new file mode 100644 index 0000000..bb0a5ce --- /dev/null +++ b/usr/include/arch/sparc64/klibc/archsignal.h @@ -0,0 +1,17 @@ +/* + * arch/sparc64/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#define __WANT_POSIX1B_SIGNALS__ +#include <asm/signal.h> + +/* Not actually used by the kernel... */ +#define SA_RESTORER 0x80000000 + +#endif diff --git a/usr/include/arch/sparc64/machine/asm.h b/usr/include/arch/sparc64/machine/asm.h new file mode 100644 index 0000000..394ba86 --- /dev/null +++ b/usr/include/arch/sparc64/machine/asm.h @@ -0,0 +1 @@ +#include "../../sparc/machine/asm.h" diff --git a/usr/include/arch/sparc64/machine/frame.h b/usr/include/arch/sparc64/machine/frame.h new file mode 100644 index 0000000..79beea6 --- /dev/null +++ b/usr/include/arch/sparc64/machine/frame.h @@ -0,0 +1 @@ +#include "../../sparc/machine/frame.h" diff --git a/usr/include/arch/x86_64/klibc/archconfig.h b/usr/include/arch/x86_64/klibc/archconfig.h new file mode 100644 index 0000000..b8a2a6d --- /dev/null +++ b/usr/include/arch/x86_64/klibc/archconfig.h @@ -0,0 +1,15 @@ +/* + * include/arch/x86_64/klibc/archconfig.h + * + * See include/klibc/sysconfig.h for the options that can be set in + * this file. + * + */ + +#ifndef _KLIBC_ARCHCONFIG_H +#define _KLIBC_ARCHCONFIG_H + +/* x86-64 doesn't provide a default sigreturn. */ +#define _KLIBC_NEEDS_SA_RESTORER 1 + +#endif /* _KLIBC_ARCHCONFIG_H */ diff --git a/usr/include/arch/x86_64/klibc/archsetjmp.h b/usr/include/arch/x86_64/klibc/archsetjmp.h new file mode 100644 index 0000000..454fc60 --- /dev/null +++ b/usr/include/arch/x86_64/klibc/archsetjmp.h @@ -0,0 +1,21 @@ +/* + * arch/x86_64/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned long __rbx; + unsigned long __rsp; + unsigned long __rbp; + unsigned long __r12; + unsigned long __r13; + unsigned long __r14; + unsigned long __r15; + unsigned long __rip; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/usr/include/arch/x86_64/klibc/archsignal.h b/usr/include/arch/x86_64/klibc/archsignal.h new file mode 100644 index 0000000..d68ab82 --- /dev/null +++ b/usr/include/arch/x86_64/klibc/archsignal.h @@ -0,0 +1,64 @@ +/* + * arch/x86_64/include/klibc/archsignal.h + * + * Architecture-specific signal definitions + * + */ + +#ifndef _KLIBC_ARCHSIGNAL_H +#define _KLIBC_ARCHSIGNAL_H + +#define _NSIG 64 +#define NSIG _NSIG +typedef unsigned long sigset_t; + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +#define SA_RESTORER 0x04000000 + +#include <asm-generic/signal-defs.h> + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + __sigrestore_t sa_restorer; + sigset_t sa_mask; /* mask last for extensibility */ +}; + +#endif diff --git a/usr/include/arch/x86_64/sys/io.h b/usr/include/arch/x86_64/sys/io.h new file mode 100644 index 0000000..19ea1fc --- /dev/null +++ b/usr/include/arch/x86_64/sys/io.h @@ -0,0 +1,127 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * sys/io.h for the i386 architecture + * + * Basic I/O macros + */ + +#ifndef _SYS_IO_H +#define _SYS_IO_H 1 + +/* I/O-related system calls */ + +int iopl(int); +int ioperm(unsigned long, unsigned long, int); + +/* Basic I/O macros */ + +static __inline__ void outb(unsigned char __v, unsigned short __p) +{ + asm volatile ("outb %0,%1" : : "a" (__v), "dN"(__p)); +} + +static __inline__ void outw(unsigned short __v, unsigned short __p) +{ + asm volatile ("outw %0,%1" : : "a" (__v), "dN"(__p)); +} + +static __inline__ void outl(unsigned int __v, unsigned short __p) +{ + asm volatile ("outl %0,%1" : : "a" (__v), "dN"(__p)); +} + +static __inline__ unsigned char inb(unsigned short __p) +{ + unsigned char __v; + asm volatile ("inb %1,%0" : "=a" (__v) : "dN"(__p)); + return __v; +} + +static __inline__ unsigned short inw(unsigned short __p) +{ + unsigned short __v; + asm volatile ("inw %1,%0" : "=a" (__v) : "dN"(__p)); + return __v; +} + +static __inline__ unsigned int inl(unsigned short __p) +{ + unsigned int __v; + asm volatile ("inl %1,%0" : "=a" (__v) : "dN"(__p)); + return __v; +} + +/* String I/O macros */ + +static __inline__ void +outsb(unsigned short __p, const void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; outsb" + : "+S" (__d), "+c"(__n) + : "d"(__p)); +} + +static __inline__ void +outsw(unsigned short __p, const void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; outsw" + : "+S" (__d), "+c"(__n) + : "d"(__p)); +} + +static __inline__ void +outsl(unsigned short __p, const void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; outsl" + : "+S" (__d), "+c"(__n) + : "d"(__p)); +} + +static __inline__ void insb(unsigned short __p, void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; insb" + : "+D" (__d), "+c"(__n) + : "d"(__p)); +} + +static __inline__ void insw(unsigned short __p, void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; insw" + : "+D" (__d), "+c"(__n) + : "d"(__p)); +} + +static __inline__ void insl(unsigned short __p, void *__d, unsigned long __n) +{ + asm volatile ("cld; rep; insl" + : "+D" (__d), "+c"(__n) + : "d"(__p)); +} + +#endif /* _SYS_IO_H */ diff --git a/usr/include/arpa/inet.h b/usr/include/arpa/inet.h new file mode 100644 index 0000000..8532f67 --- /dev/null +++ b/usr/include/arpa/inet.h @@ -0,0 +1,22 @@ +/* + * arpa/inet.h + */ + +#ifndef _ARPA_INET_H +#define _ARPA_INET_H + +#include <klibc/extern.h> +#include <stdint.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netinet/in6.h> + +__extern uint32_t inet_addr(const char *); +__extern int inet_aton(const char *, struct in_addr *); +__extern char *inet_ntoa(struct in_addr); +__extern int inet_pton(int, const char *, void *); +__extern const char *inet_ntop(int, const void *, char *, size_t); +__extern unsigned int inet_nsap_addr(const char *, unsigned char *, int); +__extern char *inet_nsap_ntoa(int, const unsigned char *, char *); + +#endif /* _ARPA_INET_H */ diff --git a/usr/include/assert.h b/usr/include/assert.h new file mode 100644 index 0000000..9091733 --- /dev/null +++ b/usr/include/assert.h @@ -0,0 +1,28 @@ +/* + * assert.h + */ + +#ifndef _ASSERT_H +#define _ASSERT_H + +#include <klibc/compiler.h> + +#ifdef NDEBUG + +/* + * NDEBUG doesn't just suppress the faulting behavior of assert(), + * but also all side effects of the assert() argument. This behavior + * is required by the C standard, and allows the argument to reference + * variables that are not defined without NDEBUG. + */ +#define assert(x) ((void)(0)) + +#else + +extern __noreturn __assert_fail(const char *, const char *, unsigned int); + +#define assert(x) ((x) ? (void)0 : __assert_fail(#x, __FILE__, __LINE__)) + +#endif + +#endif /* _ASSERT_H */ diff --git a/usr/include/bits32/bitsize.h b/usr/include/bits32/bitsize.h new file mode 100644 index 0000000..06cc885 --- /dev/null +++ b/usr/include/bits32/bitsize.h @@ -0,0 +1,3 @@ +#ifndef _BITSIZE +#define _BITSIZE 32 +#endif diff --git a/usr/include/bits32/bitsize/limits.h b/usr/include/bits32/bitsize/limits.h new file mode 100644 index 0000000..8eb97d6 --- /dev/null +++ b/usr/include/bits32/bitsize/limits.h @@ -0,0 +1,14 @@ +/* + * bits32/limits.h + */ + +#ifndef _BITSIZE_LIMITS_H +#define _BITSIZE_LIMITS_H + +#define LONG_BIT 32 + +#define LONG_MIN (-2147483647L-1) +#define LONG_MAX 2147483647L +#define ULONG_MAX 4294967295UL + +#endif /* _BITSIZE_LIMITS_H */ diff --git a/usr/include/bits32/bitsize/stdint.h b/usr/include/bits32/bitsize/stdint.h new file mode 100644 index 0000000..8e444b6 --- /dev/null +++ b/usr/include/bits32/bitsize/stdint.h @@ -0,0 +1,34 @@ +/* + * bits32/stdint.h + */ + +#ifndef _BITSIZE_STDINT_H +#define _BITSIZE_STDINT_H + +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; + +typedef int int_fast16_t; +typedef int int_fast32_t; + +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; + +typedef int intptr_t; +typedef unsigned int uintptr_t; + +#define __INT64_C(c) c ## LL +#define __UINT64_C(c) c ## ULL + +#define __PRI64_RANK "ll" +#define __PRIFAST_RANK "" +#define __PRIPTR_RANK "" + +#endif /* _BITSIZE_STDINT_H */ diff --git a/usr/include/bits32/bitsize/stdintconst.h b/usr/include/bits32/bitsize/stdintconst.h new file mode 100644 index 0000000..7db63bd --- /dev/null +++ b/usr/include/bits32/bitsize/stdintconst.h @@ -0,0 +1,18 @@ +/* + * bits32/stdintconst.h + */ + +#ifndef _BITSIZE_STDINTCONST_H +#define _BITSIZE_STDINTCONST_H + +#define INT_FAST16_C(c) INT32_C(c) +#define INT_FAST32_C(c) INT32_C(c) + +#define UINT_FAST16_C(c) UINT32_C(c) +#define UINT_FAST32_C(c) UINT32_C(c) + +#define INTPTR_C(c) INT32_C(c) +#define UINTPTR_C(c) UINT32_C(c) +#define PTRDIFF_C(c) INT32_C(c) + +#endif /* _BITSIZE_STDINTCONST_H */ diff --git a/usr/include/bits32/bitsize/stdintlimits.h b/usr/include/bits32/bitsize/stdintlimits.h new file mode 100644 index 0000000..4c1c2e6 --- /dev/null +++ b/usr/include/bits32/bitsize/stdintlimits.h @@ -0,0 +1,23 @@ +/* + * bits32/stdintlimits.h + */ + +#ifndef _BITSIZE_STDINTLIMITS_H +#define _BITSIZE_STDINTLIMITS_H + +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX + +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX + +#define PTRDIFF_MIN INT32_MIN +#define PTRDIFF_MAX INT32_MAX +#define SIZE_MAX UINT32_MAX + +#endif /* _BITSIZE_STDINTLIMITS_H */ diff --git a/usr/include/bits64/bitsize.h b/usr/include/bits64/bitsize.h new file mode 100644 index 0000000..54696fd --- /dev/null +++ b/usr/include/bits64/bitsize.h @@ -0,0 +1,3 @@ +#ifndef _BITSIZE +#define _BITSIZE 64 +#endif diff --git a/usr/include/bits64/bitsize/limits.h b/usr/include/bits64/bitsize/limits.h new file mode 100644 index 0000000..f5bbf83 --- /dev/null +++ b/usr/include/bits64/bitsize/limits.h @@ -0,0 +1,14 @@ +/* + * bits64/limits.h + */ + +#ifndef _BITSIZE_LIMITS_H +#define _BITSIZE_LIMITS_H + +#define LONG_BIT 64 + +#define LONG_MIN (-9223372036854775807L-1) +#define LONG_MAX 9223372036854775807L +#define ULONG_MAX 18446744073709551615UL + +#endif /* _BITSIZE_LIMITS_H */ diff --git a/usr/include/bits64/bitsize/stdint.h b/usr/include/bits64/bitsize/stdint.h new file mode 100644 index 0000000..988e639 --- /dev/null +++ b/usr/include/bits64/bitsize/stdint.h @@ -0,0 +1,34 @@ +/* + * bits64/stdint.h + */ + +#ifndef _BITSIZE_STDINT_H +#define _BITSIZE_STDINT_H + +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long int int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; + +typedef long int int_fast16_t; +typedef long int int_fast32_t; + +typedef unsigned long int uint_fast16_t; +typedef unsigned long int uint_fast32_t; + +typedef long int intptr_t; +typedef unsigned long int uintptr_t; + +#define __INT64_C(c) c ## L +#define __UINT64_C(c) c ## UL + +#define __PRI64_RANK "l" +#define __PRIFAST_RANK "l" +#define __PRIPTR_RANK "l" + +#endif /* _BITSIZE_STDINT_H */ diff --git a/usr/include/bits64/bitsize/stdintconst.h b/usr/include/bits64/bitsize/stdintconst.h new file mode 100644 index 0000000..24e8eb6 --- /dev/null +++ b/usr/include/bits64/bitsize/stdintconst.h @@ -0,0 +1,18 @@ +/* + * bits64/stdintconst.h + */ + +#ifndef _BITSIZE_STDINTCONST_H +#define _BITSIZE_STDINTCONST_H + +#define INT_FAST16_C(c) INT64_C(c) +#define INT_FAST32_C(c) INT64_C(c) + +#define UINT_FAST16_C(c) UINT64_C(c) +#define UINT_FAST32_C(c) UINT64_C(c) + +#define INTPTR_C(c) INT64_C(c) +#define UINTPTR_C(c) UINT64_C(c) +#define PTRDIFF_C(c) INT64_C(c) + +#endif /* _BITSIZE_STDINTCONST_H */ diff --git a/usr/include/bits64/bitsize/stdintlimits.h b/usr/include/bits64/bitsize/stdintlimits.h new file mode 100644 index 0000000..4ef07f7 --- /dev/null +++ b/usr/include/bits64/bitsize/stdintlimits.h @@ -0,0 +1,23 @@ +/* + * bits64/stdintlimits.h + */ + +#ifndef _BITSIZE_STDINTLIMITS_H +#define _BITSIZE_STDINTLIMITS_H + +#define INT_FAST16_MIN INT64_MIN +#define INT_FAST32_MIN INT64_MIN +#define INT_FAST16_MAX INT64_MAX +#define INT_FAST32_MAX INT64_MAX +#define UINT_FAST16_MAX UINT64_MAX +#define UINT_FAST32_MAX UINT64_MAX + +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX + +#define PTRDIFF_MIN INT64_MIN +#define PTRDIFF_MAX INT64_MAX +#define SIZE_MAX UINT64_MAX + +#endif /* _BITSIZE_STDINTLIMITS_H */ diff --git a/usr/include/byteswap.h b/usr/include/byteswap.h new file mode 100644 index 0000000..d70f1f9 --- /dev/null +++ b/usr/include/byteswap.h @@ -0,0 +1,15 @@ +/* + * byteswap.h + */ + +#ifndef _BYTESWAP_H +#define _BYTESWAP_H + +#include <sys/types.h> +#include <asm/byteorder.h> + +#define bswap_16(x) __swab16(x) +#define bswap_32(x) __swab32(x) +#define bswap_64(x) __swab64(x) + +#endif /* _BYTESWAP_H */ diff --git a/usr/include/ctype.h b/usr/include/ctype.h new file mode 100644 index 0000000..14a2f2d --- /dev/null +++ b/usr/include/ctype.h @@ -0,0 +1,174 @@ +/* + * ctype.h + * + * This assumes ISO 8859-1, being a reasonable superset of ASCII. + */ + +#ifndef _CTYPE_H +#define _CTYPE_H + +#include <klibc/extern.h> +#include <klibc/compiler.h> + +/* + * This relies on the following definitions: + * + * cntrl = !print + * alpha = upper|lower + * graph = punct|alpha|digit + * blank = '\t' || ' ' (per POSIX requirement) + */ +enum { + __ctype_upper = (1 << 0), + __ctype_lower = (1 << 1), + __ctype_digit = (1 << 2), + __ctype_xdigit = (1 << 3), + __ctype_space = (1 << 4), + __ctype_print = (1 << 5), + __ctype_punct = (1 << 6), + __ctype_cntrl = (1 << 7), +}; + +__extern int isalnum(int); +__extern int isalpha(int); +__extern int isascii(int); +__extern int isblank(int); +__extern int iscntrl(int); +__extern int isdigit(int); +__extern int isgraph(int); +__extern int islower(int); +__extern int isprint(int); +__extern int ispunct(int); +__extern int isspace(int); +__extern int isupper(int); +__extern int isxdigit(int); +__extern int toupper(int); +__extern int tolower(int); + +extern const unsigned char __ctypes[]; + +__must_inline int __ctype_isalnum(int); +__must_inline int __ctype_isalpha(int); +__must_inline int __ctype_isascii(int); +__must_inline int __ctype_isblank(int); +__must_inline int __ctype_iscntrl(int); +__must_inline int __ctype_isdigit(int); +__must_inline int __ctype_isgraph(int); +__must_inline int __ctype_islower(int); +__must_inline int __ctype_isprint(int); +__must_inline int __ctype_ispunct(int); +__must_inline int __ctype_isspace(int); +__must_inline int __ctype_isupper(int); +__must_inline int __ctype_isxdigit(int); + +__must_inline int __ctype_isalnum(int __c) +{ + return __ctypes[__c + 1] & + (__ctype_upper | __ctype_lower | __ctype_digit); +} + +__must_inline int __ctype_isalpha(int __c) +{ + return __ctypes[__c + 1] & (__ctype_upper | __ctype_lower); +} + +__must_inline int __ctype_isascii(int __c) +{ + return !(__c & ~0x7f); +} + +__must_inline int __ctype_isblank(int __c) +{ + return (__c == '\t') || (__c == ' '); +} + +__must_inline int __ctype_iscntrl(int __c) +{ + return __ctypes[__c + 1] & __ctype_cntrl; +} + +__must_inline int __ctype_isdigit(int __c) +{ + return ((unsigned)__c - '0') <= 9; +} + +__must_inline int __ctype_isgraph(int __c) +{ + return __ctypes[__c + 1] & + (__ctype_upper | __ctype_lower | __ctype_digit | __ctype_punct); +} + +__must_inline int __ctype_islower(int __c) +{ + return __ctypes[__c + 1] & __ctype_lower; +} + +__must_inline int __ctype_isprint(int __c) +{ + return __ctypes[__c + 1] & __ctype_print; +} + +__must_inline int __ctype_ispunct(int __c) +{ + return __ctypes[__c + 1] & __ctype_punct; +} + +__must_inline int __ctype_isspace(int __c) +{ + return __ctypes[__c + 1] & __ctype_space; +} + +__must_inline int __ctype_isupper(int __c) +{ + return __ctypes[__c + 1] & __ctype_upper; +} + +__must_inline int __ctype_isxdigit(int __c) +{ + return __ctypes[__c + 1] & __ctype_xdigit; +} + +/* Note: this is decimal, not hex, to avoid accidental promotion to unsigned */ +#define _toupper(__c) ((__c) & ~32) +#define _tolower(__c) ((__c) | 32) + +__must_inline int __ctype_toupper(int); +__must_inline int __ctype_tolower(int); + +__must_inline int __ctype_toupper(int __c) +{ + return __ctype_islower(__c) ? _toupper(__c) : __c; +} + +__must_inline int __ctype_tolower(int __c) +{ + return __ctype_isupper(__c) ? _tolower(__c) : __c; +} + +#ifdef __CTYPE_NO_INLINE +# define __CTYPEFUNC(X) \ + __extern int X(int); +#else +#define __CTYPEFUNC(X) \ + __extern_inline int X(int __c) \ + { \ + return __ctype_##X(__c); \ + } +#endif + +__CTYPEFUNC(isalnum) +__CTYPEFUNC(isalpha) +__CTYPEFUNC(isascii) +__CTYPEFUNC(isblank) +__CTYPEFUNC(iscntrl) +__CTYPEFUNC(isdigit) +__CTYPEFUNC(isgraph) +__CTYPEFUNC(islower) +__CTYPEFUNC(isprint) +__CTYPEFUNC(ispunct) +__CTYPEFUNC(isspace) +__CTYPEFUNC(isupper) +__CTYPEFUNC(isxdigit) +__CTYPEFUNC(toupper) +__CTYPEFUNC(tolower) +#endif /* _CTYPE_H */ diff --git a/usr/include/dirent.h b/usr/include/dirent.h new file mode 100644 index 0000000..43df503 --- /dev/null +++ b/usr/include/dirent.h @@ -0,0 +1,43 @@ +/* + * dirent.h + */ + +#ifndef _DIRENT_H +#define _DIRENT_H + +#include <klibc/compiler.h> +#include <klibc/extern.h> +#include <klibc/sysconfig.h> +#include <sys/dirent.h> + +struct _IO_dir { + int __fd; + +#ifdef __KLIBC_DIRENT_INTERNALS + /* These fields for internal use only */ + + size_t bytes_left; + struct dirent *next; + /* Declaring this as an array of struct enforces correct alignment */ + struct dirent buffer[_KLIBC_BUFSIZ / sizeof(struct dirent)]; +#endif +}; +typedef struct _IO_dir DIR; + +__extern DIR *fdopendir(int); +__extern DIR *opendir(const char *); +__extern struct dirent *readdir(DIR *); +__extern int closedir(DIR *); +__static_inline int dirfd(DIR * __d) +{ + return __d->__fd; +} + +__extern int scandir(const char *, struct dirent ***, + int (*)(const struct dirent *), + int (*)(const struct dirent **, + const struct dirent **)); + +__extern int alphasort(const struct dirent **, const struct dirent **); + +#endif /* _DIRENT_H */ diff --git a/usr/include/elf.h b/usr/include/elf.h new file mode 100644 index 0000000..c543a81 --- /dev/null +++ b/usr/include/elf.h @@ -0,0 +1,11 @@ +/* + * elf.h + */ + +#ifndef _ELF_H +#define _ELF_H + +#include <sys/elf32.h> +#include <sys/elf64.h> + +#endif /* _ELF_H */ diff --git a/usr/include/endian.h b/usr/include/endian.h new file mode 100644 index 0000000..61cda3a --- /dev/null +++ b/usr/include/endian.h @@ -0,0 +1,21 @@ +/* + * endian.h + */ + +#ifndef _ENDIAN_H +#define _ENDIAN_H + +#include <klibc/endian.h> + +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN +#define PDP_ENDIAN __PDP_ENDIAN +#define BYTE_ORDER __BYTE_ORDER + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define __LONG_LONG_PAIR(HI, LO) LO, HI +#elif __BYTE_ORDER == __BIG_ENDIAN +# define __LONG_LONG_PAIR(HI, LO) HI, LO +#endif + +#endif /* _ENDIAN_H */ diff --git a/usr/include/errno.h b/usr/include/errno.h new file mode 100644 index 0000000..d8e20bd --- /dev/null +++ b/usr/include/errno.h @@ -0,0 +1,13 @@ +/* + * errno.h + */ + +#ifndef _ERRNO_H +#define _ERRNO_H + +#include <klibc/extern.h> +#include <asm/errno.h> + +__extern int errno; + +#endif /* _ERRNO_H */ diff --git a/usr/include/fcntl.h b/usr/include/fcntl.h new file mode 100644 index 0000000..cb2e4e5 --- /dev/null +++ b/usr/include/fcntl.h @@ -0,0 +1,67 @@ +/* + * fcntl.h + */ + +#ifndef _FCNTL_H +#define _FCNTL_H + +#include <klibc/extern.h> +#include <klibc/compiler.h> +#include <klibc/seek.h> +#include <sys/types.h> +#if defined(__mips__) && ! defined(__mips64) +# include <klibc/archfcntl.h> +#elif _BITSIZE == 32 +/* We want a struct flock with 64-bit offsets, which we define below */ +# define HAVE_ARCH_STRUCT_FLOCK +#endif +#include <linux/fcntl.h> +#include <bitsize.h> + +#if !defined(__mips__) && _BITSIZE == 32 + +/* + * <linux/fcntl.h> defines struct flock with offsets of type + * __kernel_off_t (= long) and struct flock64 with offsets of + * type __kernel_loff_t (= long long). We want struct flock + * to have 64-bit offsets, so we define it here. + */ + +struct flock { + short l_type; + short l_whence; + __kernel_loff_t l_start; + __kernel_loff_t l_len; + __kernel_pid_t l_pid; +#ifdef __ARCH_FLOCK64_PAD + __ARCH_FLOCK64_PAD +#endif +}; + +#ifdef F_GETLK64 +# undef F_GETLK +# define F_GETLK F_GETLK64 +#endif + +#ifdef F_SETLK64 +# undef F_SETLK +# define F_SETLK F_SETLK64 +#endif + +#ifdef F_SETLKW64 +# undef F_SETLKW +# define F_SETLKW F_SETLKW64 +#endif + +#endif /* _BITSIZE == 32 */ + +/* This is defined here as well as in <unistd.h> */ +#ifndef _KLIBC_IN_OPEN_C +__extern int open(const char *, int, ...); +__extern int openat(int, const char *, int, ...); +#endif + +__extern int creat(const char *, mode_t); +__extern int fcntl(int, int, ...); + +#endif /* _FCNTL_H */ diff --git a/usr/include/fnmatch.h b/usr/include/fnmatch.h new file mode 100644 index 0000000..0d88140 --- /dev/null +++ b/usr/include/fnmatch.h @@ -0,0 +1,15 @@ +#ifndef _FNMATCH_H +#define _FNMATCH_H + +#include <klibc/extern.h> + +#define FNM_NOMATCH 1 + +#define FNM_PATHNAME 1 +#define FNM_FILE_NAME FNM_PATHNAME +#define FNM_NOESCAPE 2 +#define FNM_PERIOD 4 + +__extern int fnmatch(const char *, const char *, int); + +#endif /* _FNMATCH_H */ diff --git a/usr/include/getopt.h b/usr/include/getopt.h new file mode 100644 index 0000000..71c41cd --- /dev/null +++ b/usr/include/getopt.h @@ -0,0 +1,22 @@ +#ifndef _GETOPT_H +#define _GETOPT_H + +#include <klibc/extern.h> + +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +enum { + no_argument = 0, + required_argument = 1, + optional_argument = 2, +}; + +__extern int getopt_long(int, char *const *, const char *, + const struct option *, int *); + +#endif /* _GETOPT_H */ diff --git a/usr/include/grp.h b/usr/include/grp.h new file mode 100644 index 0000000..77ce399 --- /dev/null +++ b/usr/include/grp.h @@ -0,0 +1,22 @@ +/* + * grp.h + */ + +#ifndef _GRP_H +#define _GRP_H + +#include <klibc/extern.h> +#include <sys/types.h> + +struct group { + char *gr_name; + char *gr_passwd; + gid_t gr_gid; + char **gr_mem; +}; + +__extern int setgroups(size_t, const gid_t *); +__extern struct group *getgrgid(gid_t); +__extern struct group *getgrnam(const char *); + +#endif /* _GRP_H */ diff --git a/usr/include/inttypes.h b/usr/include/inttypes.h new file mode 100644 index 0000000..29311fe --- /dev/null +++ b/usr/include/inttypes.h @@ -0,0 +1,226 @@ +/* + * inttypes.h + */ + +#ifndef _INTTYPES_H +#define _INTTYPES_H + +#include <klibc/extern.h> +#include <stdint.h> +#include <stddef.h> + +static __inline__ intmax_t imaxabs(intmax_t __n) +{ + return (__n < (intmax_t) 0) ? -__n : __n; +} + +__extern intmax_t strtoimax(const char *, char **, int); +__extern uintmax_t strtoumax(const char *, char **, int); + +/* extensions */ +__extern intmax_t strntoimax(const char *, char **, int, size_t); +__extern uintmax_t strntoumax(const char *, char **, int, size_t); + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) + +#define PRId8 "d" +#define PRId16 "d" +#define PRId32 "d" +#define PRId64 __PRI64_RANK "d" + +#define PRIdLEAST8 "d" +#define PRIdLEAST16 "d" +#define PRIdLEAST32 "d" +#define PRIdLEAST64 __PRI64_RANK "d" + +#define PRIdFAST8 "d" +#define PRIdFAST16 __PRIFAST_RANK "d" +#define PRIdFAST32 __PRIFAST_RANK "d" +#define PRIdFAST64 __PRI64_RANK "d" + +#define PRIdMAX __PRI64_RANK "d" +#define PRIdPTR __PRIPTR_RANK "d" + +#define PRIi8 "i" +#define PRIi16 "i" +#define PRIi32 "i" +#define PRIi64 __PRI64_RANK "i" + +#define PRIiLEAST8 "i" +#define PRIiLEAST16 "i" +#define PRIiLEAST32 "i" +#define PRIiLEAST64 __PRI64_RANK "i" + +#define PRIiFAST8 "i" +#define PRIiFAST16 __PRIFAST_RANK "i" +#define PRIiFAST32 __PRIFAST_RANK "i" +#define PRIiFAST64 __PRI64_RANK "i" + +#define PRIiMAX __PRI64_RANK "i" +#define PRIiPTR __PRIPTR_RANK "i" + +#define PRIo8 "o" +#define PRIo16 "o" +#define PRIo32 "o" +#define PRIo64 __PRI64_RANK "o" + +#define PRIoLEAST8 "o" +#define PRIoLEAST16 "o" +#define PRIoLEAST32 "o" +#define PRIoLEAST64 __PRI64_RANK "o" + +#define PRIoFAST8 "o" +#define PRIoFAST16 __PRIFAST_RANK "o" +#define PRIoFAST32 __PRIFAST_RANK "o" +#define PRIoFAST64 __PRI64_RANK "o" + +#define PRIoMAX __PRI64_RANK "o" +#define PRIoPTR __PRIPTR_RANK "o" + +#define PRIu8 "u" +#define PRIu16 "u" +#define PRIu32 "u" +#define PRIu64 __PRI64_RANK "u" + +#define PRIuLEAST8 "u" +#define PRIuLEAST16 "u" +#define PRIuLEAST32 "u" +#define PRIuLEAST64 __PRI64_RANK "u" + +#define PRIuFAST8 "u" +#define PRIuFAST16 __PRIFAST_RANK "u" +#define PRIuFAST32 __PRIFAST_RANK "u" +#define PRIuFAST64 __PRI64_RANK "u" + +#define PRIuMAX __PRI64_RANK "u" +#define PRIuPTR __PRIPTR_RANK "u" + +#define PRIx8 "x" +#define PRIx16 "x" +#define PRIx32 "x" +#define PRIx64 __PRI64_RANK "x" + +#define PRIxLEAST8 "x" +#define PRIxLEAST16 "x" +#define PRIxLEAST32 "x" +#define PRIxLEAST64 __PRI64_RANK "x" + +#define PRIxFAST8 "x" +#define PRIxFAST16 __PRIFAST_RANK "x" +#define PRIxFAST32 __PRIFAST_RANK "x" +#define PRIxFAST64 __PRI64_RANK "x" + +#define PRIxMAX __PRI64_RANK "x" +#define PRIxPTR __PRIPTR_RANK "x" + +#define PRIX8 "X" +#define PRIX16 "X" +#define PRIX32 "X" +#define PRIX64 __PRI64_RANK "X" + +#define PRIXLEAST8 "X" +#define PRIXLEAST16 "X" +#define PRIXLEAST32 "X" +#define PRIXLEAST64 __PRI64_RANK "X" + +#define PRIXFAST8 "X" +#define PRIXFAST16 __PRIFAST_RANK "X" +#define PRIXFAST32 __PRIFAST_RANK "X" +#define PRIXFAST64 __PRI64_RANK "X" + +#define PRIXMAX __PRI64_RANK "X" +#define PRIXPTR __PRIPTR_RANK "X" + +#define SCNd8 "hhd" +#define SCNd16 "hd" +#define SCNd32 "d" +#define SCNd64 __PRI64_RANK "d" + +#define SCNdLEAST8 "hhd" +#define SCNdLEAST16 "hd" +#define SCNdLEAST32 "d" +#define SCNdLEAST64 __PRI64_RANK "d" + +#define SCNdFAST8 "hhd" +#define SCNdFAST16 __PRIFAST_RANK "d" +#define SCNdFAST32 __PRIFAST_RANK "d" +#define SCNdFAST64 __PRI64_RANK "d" + +#define SCNdMAX __PRI64_RANK "d" +#define SCNdPTR __PRIPTR_RANK "d" + +#define SCNi8 "hhi" +#define SCNi16 "hi" +#define SCNi32 "i" +#define SCNi64 __PRI64_RANK "i" + +#define SCNiLEAST8 "hhi" +#define SCNiLEAST16 "hi" +#define SCNiLEAST32 "i" +#define SCNiLEAST64 __PRI64_RANK "i" + +#define SCNiFAST8 "hhi" +#define SCNiFAST16 __PRIFAST_RANK "i" +#define SCNiFAST32 __PRIFAST_RANK "i" +#define SCNiFAST64 __PRI64_RANK "i" + +#define SCNiMAX __PRI64_RANK "i" +#define SCNiPTR __PRIPTR_RANK "i" + +#define SCNo8 "hho" +#define SCNo16 "ho" +#define SCNo32 "o" +#define SCNo64 __PRI64_RANK "o" + +#define SCNoLEAST8 "hho" +#define SCNoLEAST16 "ho" +#define SCNoLEAST32 "o" +#define SCNoLEAST64 __PRI64_RANK "o" + +#define SCNoFAST8 "hho" +#define SCNoFAST16 __PRIFAST_RANK "o" +#define SCNoFAST32 __PRIFAST_RANK "o" +#define SCNoFAST64 __PRI64_RANK "o" + +#define SCNoMAX __PRI64_RANK "o" +#define SCNoPTR __PRIPTR_RANK "o" + +#define SCNu8 "hhu" +#define SCNu16 "hu" +#define SCNu32 "u" +#define SCNu64 __PRI64_RANK "u" + +#define SCNuLEAST8 "hhu" +#define SCNuLEAST16 "hu" +#define SCNuLEAST32 "u" +#define SCNuLEAST64 __PRI64_RANK "u" + +#define SCNuFAST8 "hhu" +#define SCNuFAST16 __PRIFAST_RANK "u" +#define SCNuFAST32 __PRIFAST_RANK "u" +#define SCNuFAST64 __PRI64_RANK "u" + +#define SCNuMAX __PRI64_RANK "u" +#define SCNuPTR __PRIPTR_RANK "u" + +#define SCNx8 "hhx" +#define SCNx16 "hx" +#define SCNx32 "x" +#define SCNx64 __PRI64_RANK "x" + +#define SCNxLEAST8 "hhx" +#define SCNxLEAST16 "hx" +#define SCNxLEAST32 "x" +#define SCNxLEAST64 __PRI64_RANK "x" + +#define SCNxFAST8 "hhx" +#define SCNxFAST16 __PRIFAST_RANK "x" +#define SCNxFAST32 __PRIFAST_RANK "x" +#define SCNxFAST64 __PRI64_RANK "x" + +#define SCNxMAX __PRI64_RANK "x" +#define SCNxPTR __PRIPTR_RANK "x" + +#endif + +#endif /* _INTTYPES_H */ diff --git a/usr/include/klibc/compiler.h b/usr/include/klibc/compiler.h new file mode 100644 index 0000000..ff5a006 --- /dev/null +++ b/usr/include/klibc/compiler.h @@ -0,0 +1,158 @@ +/* + * klibc/compiler.h + * + * Various compiler features + */ + +#ifndef _KLIBC_COMPILER_H +#define _KLIBC_COMPILER_H + +/* Specific calling conventions */ +/* __cdecl is used when we want varadic and non-varadic functions to have + the same binary calling convention. */ +#ifdef __i386__ +# ifdef __GNUC__ +# define __cdecl __attribute__((cdecl,regparm(0))) +# else + /* Most other C compilers have __cdecl as a keyword */ +# endif +#else +# define __cdecl /* Meaningless on non-i386 */ +#endif + +/* + * How to declare a function which should be inlined or instantiated locally + */ +#ifdef __GNUC__ +# ifdef __GNUC_STDC_INLINE__ +# define __static_inline static __inline__ __attribute__((__gnu_inline__)) +# else +# define __static_inline static __inline__ +# endif +#else +# define __static_inline inline /* Just hope this works... */ +#endif + +/* + * How to declare a function which should be inlined or have a call to + * an external module + */ +#ifdef __GNUC__ +# ifdef __GNUC_STDC_INLINE__ +# define __extern_inline extern __inline__ __attribute__((__gnu_inline__)) +# else +# define __extern_inline extern __inline__ +# endif +#else +# define __extern_inline inline /* Just hope this works... */ +#endif + +/* How to declare a function that *must* be inlined */ +/* Use "extern inline" even in the gcc3+ case to avoid warnings in ctype.h */ +#ifdef __GNUC__ +# if __GNUC__ >= 3 +# define __must_inline __extern_inline __attribute__((__always_inline__)) +# else +# define __must_inline extern __inline__ +# endif +#else +# define __must_inline inline /* Just hope this works... */ +#endif + +/* How to declare a function that does not return */ +#ifdef __GNUC__ +# define __noreturn void __attribute__((noreturn)) +#else +# define __noreturn void +#endif + +/* "const" function: + + Many functions do not examine any values except their arguments, + and have no effects except the return value. Basically this is + just slightly more strict class than the `pure' attribute above, + since function is not allowed to read global memory. + + Note that a function that has pointer arguments and examines the + data pointed to must _not_ be declared `const'. Likewise, a + function that calls a non-`const' function usually must not be + `const'. It does not make sense for a `const' function to return + `void'. +*/ +#ifdef __GNUC__ +# define __constfunc __attribute__((const)) +#else +# define __constfunc +#endif +#undef __attribute_const__ +#define __attribute_const__ __constfunc + +/* "pure" function: + + Many functions have no effects except the return value and their + return value depends only on the parameters and/or global + variables. Such a function can be subject to common subexpression + elimination and loop optimization just as an arithmetic operator + would be. These functions should be declared with the attribute + `pure'. +*/ +#ifdef __GNUC__ +# define __purefunc __attribute__((pure)) +#else +# define __purefunc +#endif +#undef __attribute_pure__ +#define __attribute_pure__ __purefunc + +/* Format attribute */ +#ifdef __GNUC__ +# define __formatfunc(t,f,a) __attribute__((format(t,f,a))) +#else +# define __formatfunc(t,f,a) +#endif + +/* malloc() function (returns unaliased pointer) */ +#if defined(__GNUC__) && (__GNUC__ >= 3) +# define __mallocfunc __attribute__((malloc)) +#else +# define __mallocfunc +#endif + +/* likely/unlikely */ +#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)) +# define __likely(x) __builtin_expect(!!(x), 1) +# define __unlikely(x) __builtin_expect(!!(x), 0) +#else +# define __likely(x) (!!(x)) +# define __unlikely(x) (!!(x)) +#endif + +/* Possibly unused function */ +#ifdef __GNUC__ +# define __unusedfunc __attribute__((unused)) +#else +# define __unusedfunc +#endif + +/* It's all user space... */ +#define __user + +/* The bitwise attribute: disallow arithmetric operations */ +#ifdef __CHECKER__ /* sparse only */ +# define __bitwise __attribute__((bitwise)) +#else +# define __bitwise +#endif + +/* Shut up unused warnings */ +#ifdef __GNUC__ +# define __attribute_used__ __attribute__((used)) +#else +# define __attribute_used__ +#endif + +/* Compiler pragma to make an alias symbol */ +#define __ALIAS(__t, __f, __p, __a) \ + __t __f __p __attribute__((weak, alias(#__a))); + +#endif diff --git a/usr/include/klibc/diverr.h b/usr/include/klibc/diverr.h new file mode 100644 index 0000000..e70887e --- /dev/null +++ b/usr/include/klibc/diverr.h @@ -0,0 +1,15 @@ +/* + * klibc/diverr.h + */ + +#ifndef _KLIBC_DIVERR_H +#define _KLIBC_DIVERR_H + +#include <signal.h> + +static __inline__ void __divide_error(void) +{ + raise(SIGFPE); +} + +#endif /* _KLIBC_DIVERR_H */ diff --git a/usr/include/klibc/endian.h b/usr/include/klibc/endian.h new file mode 100644 index 0000000..8586888 --- /dev/null +++ b/usr/include/klibc/endian.h @@ -0,0 +1,39 @@ +/* + * klibc/endian.h + * + * Like <endian.h>, but export only double-underscore symbols + */ + +#ifndef _KLIBC_ENDIAN_H +#define _KLIBC_ENDIAN_H + +#include <sys/types.h> +#include <asm/byteorder.h> + +/* Linux' asm/byteorder.h defines either __LITTLE_ENDIAN or + __BIG_ENDIAN, but the glibc/BSD-ish macros expect both to be + defined with __BYTE_ORDER defining which is actually used... */ + +#if defined(__LITTLE_ENDIAN) +# undef __LITTLE_ENDIAN +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __PDP_ENDIAN 3412 +# define __BYTE_ORDER __LITTLE_ENDIAN +#elif defined(__BIG_ENDIAN) +# undef __BIG_ENDIAN +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __PDP_ENDIAN 3412 +# define __BYTE_ORDER __BIG_ENDIAN +#elif defined(__PDP_ENDIAN) +# undef __PDP_ENDIAN +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __PDP_ENDIAN 3412 +# define __BYTE_ORDER __PDP_ENDIAN +#else +# error "Unknown byte order!" +#endif + +#endif /* _KLIBC_ENDIAN_H */ diff --git a/usr/include/klibc/extern.h b/usr/include/klibc/extern.h new file mode 100644 index 0000000..7d7c7b8 --- /dev/null +++ b/usr/include/klibc/extern.h @@ -0,0 +1,16 @@ +/* + * klibc/extern.h + */ + +#ifndef _KLIBC_EXTERN_H +#define _KLIBC_EXTERN_H + +#ifdef __cplusplus +#define __extern extern "C" +#else +#define __extern extern +#endif + +#define __alias(x) __attribute__((weak, alias(x))) + +#endif /* _KLIBC_EXTERN_H */ diff --git a/usr/include/klibc/seek.h b/usr/include/klibc/seek.h new file mode 100644 index 0000000..e8ffe25 --- /dev/null +++ b/usr/include/klibc/seek.h @@ -0,0 +1,14 @@ +/* + * klibc/seek.h + * + * SEEK constants - needed by stdio.h, fcntl.h, and unistd.h + */ + +#ifndef _KLIBC_SEEK_H +#define _KLIBC_SEEK_H + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#endif /* _KLIBC_SEEK_H */ diff --git a/usr/include/klibc/sysconfig.h b/usr/include/klibc/sysconfig.h new file mode 100644 index 0000000..5d16f61 --- /dev/null +++ b/usr/include/klibc/sysconfig.h @@ -0,0 +1,235 @@ +/* + * klibc/sysconfig.h + * + * Allows for definitions of some things which may be system-dependent + * NOTE: this file must not result in any output from the preprocessor. + */ + +#ifndef _KLIBC_SYSCONFIG_H +#define _KLIBC_SYSCONFIG_H + +#include <klibc/archconfig.h> +#include <asm/unistd.h> + +/* + * These are the variables that can be defined in <klibc/archconfig.h>. + * For boolean options, #define to 0 to disable, #define to 1 to enable. + * + * If undefined, they will be given defaults here. + */ + + +/* + * _KLIBC_NO_MMU: + * + * Indicates this architecture doesn't have an MMU, and therefore + * does not have the sys_fork and sys_brk system calls. + */ +/* Default to having an MMU if we can find the fork system call */ +#ifndef _KLIBC_NO_MMU +# if defined(__NR_fork) +# define _KLIBC_NO_MMU 0 +# else +# define _KLIBC_NO_MMU 1 +# endif +#endif + + +/* + * _KLIBC_REAL_VFORK: + * + * Indicates that this architecture has a real vfork() system call. + * This is the default if sys_vfork exists; if there is an + * architecture-dependent implementation of vfork(), define this + * symbol. + */ +#ifndef _KLIBC_REAL_VFORK +# if defined(__NR_vfork) +# define _KLIBC_REAL_VFORK 1 +# else +# define _KLIBC_REAL_VFORK 0 +# endif +#endif + + +/* + * _KLIBC_USE_MMAP2: + * + * Indicates that this architecture should use sys_mmap2 instead + * of sys_mmap. This is the default on 32-bit architectures, assuming + * sys_mmap2 exists. + */ +#ifndef _KLIBC_USE_MMAP2 +# if (_BITSIZE == 32 && defined(__NR_mmap2)) || \ + (_BITSIZE == 64 && !defined(__NR_mmap)) +# define _KLIBC_USE_MMAP2 1 +# else +# define _KLIBC_USE_MMAP2 0 +# endif +#endif + + +/* + * _KLIBC_MMAP2_SHIFT: + * + * Indicate the shift of the offset parameter in sys_mmap2. + * On most architectures, this is always 12, but on some + * architectures it can be a different number, or the current + * page size. If this is dependent on the page size, define + * this to an expression which includes __getpageshift(). + */ +#ifndef _KLIBC_MMAP2_SHIFT +# define _KLIBC_MMAP2_SHIFT 12 +#endif + + +/* + * _KLIBC_MALLOC_USES_SBRK: + * + * Indicates that malloc() should use sbrk() to obtain raw memory + * from the system, rather than mmap(). + */ +/* Default to get memory using mmap() */ +#ifndef _KLIBC_MALLOC_USES_SBRK +# define _KLIBC_MALLOC_USES_SBRK 0 +#endif + + +/* + * _KLIBC_MALLOC_CHUNK_SIZE: + * This is the minimum chunk size we will ask the kernel for using + * malloc(); this should be a multiple of the page size and must + * be a power of 2. + */ +#ifndef _KLIBC_MALLOC_CHUNK_SIZE +# define _KLIBC_MALLOC_CHUNK_SIZE 65536 +#endif + + +/* + * _KLIBC_BUFSIZ: + * This is the size of an stdio buffer. By default this is + * _KLIBC_MALLOC_CHUNK_SIZE/4, which allows the three standard + * streams to fit inside a malloc chunk. + */ +#ifndef _KLIBC_BUFSIZ +# define _KLIBC_BUFSIZ (_KLIBC_MALLOC_CHUNK_SIZE >> 2) +#endif + + +/* + * _KLIBC_SBRK_ALIGNMENT: + * + * This is the minimum alignment for the memory returned by + * sbrk(). It must be a power of 2. If _KLIBC_MALLOC_USES_SBRK + * is set it should be no smaller than the size of struct + * arena_header in malloc.h (== 4 pointers.) + */ +#ifndef _KLIBC_SBRK_ALIGNMENT +# define _KLIBC_SBRK_ALIGNMENT 32 +#endif + + +/* + * _KLIBC_NEEDS_SA_RESTORER: + * + * Some architectures, like x86-64 and some i386 Fedora kernels, + * do not provide a default sigreturn, and therefore must have + * SA_RESTORER set. On others, the default sigreturn requires an + * executable stack, which we should avoid. + */ +#ifndef _KLIBC_NEEDS_SA_RESTORER +# define _KLIBC_NEEDS_SA_RESTORER 0 +#endif + + +/* + * _KLIBC_NEEDS_SA_SIGINFO: + * + * On some architectures, the signal stack frame is set up for + * either sigreturn() or rt_sigreturn() depending on whether + * SA_SIGINFO is set. Where this is the case, and we provide our + * own restorer function, this must also be set so that the + * restorer can always use rt_sigreturn(). + */ +#ifndef _KLIBC_NEEDS_SA_SIGINFO +# define _KLIBC_NEEDS_SA_SIGINFO 0 +#endif + + +/* + * _KLIBC_NEEDS_SIGACTION_FIXUP + * + * On some architectures, struct sigaction needs additional + * changes before passing to the kernel. + */ +#ifndef _KLIBC_NEEDS_SIGACTION_FIXUP +# define _KLIBC_NEEDS_SIGACTION_FIXUP 0 +#endif + + +/* + * _KLIBC_STATFS_F_TYPE_64: + * + * This indicates that the f_type, f_bsize, f_namelen, + * f_frsize, and f_spare fields of struct statfs are + * 64 bits long. This is normally the case for 64-bit + * platforms, and so is the default for those. See + * usr/include/sys/vfs.h for the exact details. + */ +#ifndef _KLIBC_STATFS_F_TYPE_64 +# define _KLIBC_STATFS_F_TYPE_64 (_BITSIZE == 64) +#endif + + +/* + * _KLIBC_STATFS_F_TYPE_32B: + * + * mips has it's own definition of statfs, which is + * different from any other 32 bit arch. + */ +#ifndef _KLIBC_STATFS_F_TYPE_32B +# define _KLIBC_STATFS_F_TYPE_32B 0 +#endif + + +/* + * _KLIBC_HAS_ARCHSOCKET_H + * + * This architecture has <klibc/archsocket.h> + */ +#ifndef _KLIBC_HAS_ARCHSOCKET_H +# define _KLIBC_HAS_ARCHSOCKET_H 0 +#endif + + +/* + * _KLIBC_SYS_SOCKETCALL + * + * This architecture (e.g. SPARC) advertises socket-related + * system calls, which are not actually implemented. Use + * socketcalls unconditionally instead. + */ +#ifndef _KLIBC_SYS_SOCKETCALL +# define _KLIBC_SYS_SOCKETCALL 0 +#endif + +/* + * _KLIBC_ARM_USE_BX + * + * This arm architecture supports bx instruction. + */ +#ifndef _KLIBC_ARM_USE_BX +# define _KLIBC_ARM_USE_BX 0 +#endif + +/* + * _KLIBC_HAS_ARCHINIT + * + * This architecture has klibc/archinit.h and __libc_archinit() + */ +#ifndef _KLIBC_HAS_ARCHINIT +# define _KLIBC_HAS_ARCHINIT 0 +#endif + +#endif /* _KLIBC_SYSCONFIG_H */ diff --git a/usr/include/limits.h b/usr/include/limits.h new file mode 100644 index 0000000..464420a --- /dev/null +++ b/usr/include/limits.h @@ -0,0 +1,47 @@ +/* + * limits.h + */ + +#ifndef _LIMITS_H +#define _LIMITS_H + +/* No multibyte characters seen */ +#define MB_LEN_MAX 1 + +#define OPEN_MAX 256 + +#define CHAR_BIT 8 +#define SHRT_BIT 16 +#define INT_BIT 32 +#define LONGLONG_BIT 64 + +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 +#define UCHAR_MAX 255 + +#ifdef __CHAR_UNSIGNED__ +# define CHAR_MIN 0 +# define CHAR_MAX UCHAR_MAX +#else +# define CHAR_MIN SCHAR_MIN +# define CHAR_MAX SCHAR_MAX +#endif + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define USHRT_MAX 65535 + +#define INT_MIN (-2147483647-1) +#define INT_MAX 2147483647 +#define UINT_MAX 4294967295U + +#define LLONG_MIN (-9223372036854775807LL-1) +#define LLONG_MAX 9223372036854775807LL +#define ULLONG_MAX 18446744073709551615ULL + +#include <bitsize/limits.h> +#include <linux/limits.h> + +#define SSIZE_MAX LONG_MAX + +#endif /* _LIMITS_H */ diff --git a/usr/include/malloc.h b/usr/include/malloc.h new file mode 100644 index 0000000..eb5fe34 --- /dev/null +++ b/usr/include/malloc.h @@ -0,0 +1,22 @@ +/* + * malloc.h + * + * Apparently people haven't caught on to use <stdlib.h>, which is the + * standard place for this crap since the 1980's... + */ + +#ifndef _MALLOC_H +#define _MALLOC_H + +#include <klibc/extern.h> +#include <klibc/compiler.h> +#include <stddef.h> + +__extern void free(void *); + +__extern __mallocfunc void *malloc(size_t); +__extern __mallocfunc void *zalloc(size_t); +__extern __mallocfunc void *calloc(size_t, size_t); +__extern __mallocfunc void *realloc(void *, size_t); + +#endif /* _MALLOC_H */ diff --git a/usr/include/mntent.h b/usr/include/mntent.h new file mode 100644 index 0000000..104f35e --- /dev/null +++ b/usr/include/mntent.h @@ -0,0 +1,21 @@ +#ifndef _MNTENT_H +#define _MNTENT_H 1 + +#define MNTTYPE_SWAP "swap" + +struct mntent { + char *mnt_fsname; /* name of mounted file system */ + char *mnt_dir; /* file system path prefix */ + char *mnt_type; /* mount type (see mntent.h) */ + char *mnt_opts; /* mount options (see mntent.h) */ + int mnt_freq; /* dump frequency in days */ + int mnt_passno; /* pass number on parallel fsck */ +}; + +extern FILE *setmntent(const char *, const char *); + +extern struct mntent *getmntent(FILE *); + +extern int endmntent(FILE *fp); + +#endif /* mntent.h */ diff --git a/usr/include/net/if.h b/usr/include/net/if.h new file mode 100644 index 0000000..116a176 --- /dev/null +++ b/usr/include/net/if.h @@ -0,0 +1,8 @@ +#ifndef _NET_IF_H +#define _NET_IF_H + +#include <sys/socket.h> +#include <sys/types.h> +#include <linux/if.h> + +#endif /* _NET_IF_H */ diff --git a/usr/include/net/if_arp.h b/usr/include/net/if_arp.h new file mode 100644 index 0000000..6261eeb --- /dev/null +++ b/usr/include/net/if_arp.h @@ -0,0 +1,3 @@ +/* if_arp.h needs sockaddr */ +#include <sys/socket.h> +#include <linux/if_arp.h> diff --git a/usr/include/net/if_packet.h b/usr/include/net/if_packet.h new file mode 100644 index 0000000..33ee0aa --- /dev/null +++ b/usr/include/net/if_packet.h @@ -0,0 +1,2 @@ +#include <sys/types.h> +#include <linux/if_packet.h> diff --git a/usr/include/net/route.h b/usr/include/net/route.h new file mode 100644 index 0000000..8b4305b --- /dev/null +++ b/usr/include/net/route.h @@ -0,0 +1,2 @@ +#include <sys/types.h> +#include <linux/route.h> diff --git a/usr/include/netinet/if_ether.h b/usr/include/netinet/if_ether.h new file mode 100644 index 0000000..a0b8cde --- /dev/null +++ b/usr/include/netinet/if_ether.h @@ -0,0 +1,2 @@ +#include <sys/types.h> +#include <linux/if_ether.h> diff --git a/usr/include/netinet/in.h b/usr/include/netinet/in.h new file mode 100644 index 0000000..7a13d2d --- /dev/null +++ b/usr/include/netinet/in.h @@ -0,0 +1,38 @@ +/* + * netinet/in.h + */ + +#ifndef _NETINET_IN_H +#define _NETINET_IN_H + +#include <sys/types.h> +#include <klibc/extern.h> +#include <stdint.h> +#include <endian.h> /* Must be included *before* <linux/in.h> */ +#include <sys/socket.h> /* Must be included *before* <linux/in.h> */ +#include <linux/in.h> + +#ifndef htons +# define htons(x) __cpu_to_be16(x) +#endif +#ifndef ntohs +# define ntohs(x) __be16_to_cpu(x) +#endif +#ifndef htonl +# define htonl(x) __cpu_to_be32(x) +#endif +#ifndef ntohl +# define ntohl(x) __be32_to_cpu(x) +#endif +#ifndef htonq +# define htonq(x) __cpu_to_be64(x) +#endif +#ifndef ntohq +# define ntohq(x) __be64_to_cpu(x) +#endif + +#define IPPORT_RESERVED 1024 + +__extern int bindresvport(int sd, struct sockaddr_in *sin); + +#endif /* _NETINET_IN_H */ diff --git a/usr/include/netinet/in6.h b/usr/include/netinet/in6.h new file mode 100644 index 0000000..91a4e12 --- /dev/null +++ b/usr/include/netinet/in6.h @@ -0,0 +1,11 @@ +/* + * netinet/in6.h + */ + +#ifndef _NETINET_IN6_H +#define _NETINET_IN6_H + +#include <sys/types.h> +#include <linux/in6.h> + +#endif /* _NETINET_IN6_H */ diff --git a/usr/include/netinet/ip.h b/usr/include/netinet/ip.h new file mode 100644 index 0000000..4684bfd --- /dev/null +++ b/usr/include/netinet/ip.h @@ -0,0 +1,13 @@ +/* + * netinet/ip.h + */ + +#ifndef _NETINET_IP_H +#define _NETINET_IP_H + +#include <endian.h> +#include <linux/ip.h> + +#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ + +#endif /* _NETINET_IP_H */ diff --git a/usr/include/netinet/tcp.h b/usr/include/netinet/tcp.h new file mode 100644 index 0000000..7fc4729 --- /dev/null +++ b/usr/include/netinet/tcp.h @@ -0,0 +1,11 @@ +/* + * netinet/tcp.h + */ + +#ifndef _NETINET_TCP_H +#define _NETINET_TCP_H + +#include <endian.h> /* Include *before* linux/tcp.h */ +#include <linux/tcp.h> + +#endif /* _NETINET_TCP_H */ diff --git a/usr/include/netinet/udp.h b/usr/include/netinet/udp.h new file mode 100644 index 0000000..036f588 --- /dev/null +++ b/usr/include/netinet/udp.h @@ -0,0 +1,19 @@ +/* + * netinet/udp.h + */ + +#ifndef _NETINET_UDP_H +#define _NETINET_UDP_H + +/* + * We would include linux/udp.h, but it brings in too much other stuff + */ + +struct udphdr { + __u16 source; + __u16 dest; + __u16 len; + __u16 check; +}; + +#endif /* _NETINET_UDP_H */ diff --git a/usr/include/netpacket/packet.h b/usr/include/netpacket/packet.h new file mode 100644 index 0000000..33ee0aa --- /dev/null +++ b/usr/include/netpacket/packet.h @@ -0,0 +1,2 @@ +#include <sys/types.h> +#include <linux/if_packet.h> diff --git a/usr/include/paths.h b/usr/include/paths.h new file mode 100644 index 0000000..29ef301 --- /dev/null +++ b/usr/include/paths.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. 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. + * 4. 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. + * + * @(#)paths.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _PATHS_H_ +#define _PATHS_H_ + +/* Default search path. */ +#define _PATH_DEFPATH "/usr/bin:/bin" +/* All standard utilities path. */ +#define _PATH_STDPATH \ + "/usr/bin:/bin:/usr/sbin:/sbin" + +#define _PATH_BSHELL "/bin/sh" +#define _PATH_CONSOLE "/dev/console" +#define _PATH_CSHELL "/bin/csh" +#define _PATH_DEVDB "/var/run/dev.db" +#define _PATH_DEVNULL "/dev/null" +#define _PATH_DRUM "/dev/drum" +#define _PATH_KLOG "/proc/kmsg" +#define _PATH_KMEM "/dev/kmem" +#define _PATH_LASTLOG "/var/log/lastlog" +#define _PATH_MAILDIR "/var/mail" +#define _PATH_MAN "/usr/share/man" +#define _PATH_MEM "/dev/mem" +#define _PATH_MNTTAB "/etc/fstab" +#define _PATH_MOUNTED "/etc/mtab" +#define _PATH_NOLOGIN "/etc/nologin" +#define _PATH_PRESERVE "/var/lib" +#define _PATH_RWHODIR "/var/spool/rwho" +#define _PATH_SENDMAIL "/usr/sbin/sendmail" +#define _PATH_SHADOW "/etc/shadow" +#define _PATH_SHELLS "/etc/shells" +#define _PATH_TTY "/dev/tty" +#define _PATH_UNIX "/boot/vmlinux" +#define _PATH_UTMP "/var/run/utmp" +#define _PATH_VI "/bin/vi" +#define _PATH_WTMP "/var/log/wtmp" + +/* Provide trailing slash, since mostly used for building pathnames. */ +#define _PATH_DEV "/dev/" +#define _PATH_TMP "/tmp/" +#define _PATH_VARDB "/var/db/" +#define _PATH_VARRUN "/var/run/" +#define _PATH_VARTMP "/var/tmp/" + +#endif /* !_PATHS_H_ */ diff --git a/usr/include/poll.h b/usr/include/poll.h new file mode 100644 index 0000000..06fb41a --- /dev/null +++ b/usr/include/poll.h @@ -0,0 +1 @@ +#include <sys/poll.h> diff --git a/usr/include/pwd.h b/usr/include/pwd.h new file mode 100644 index 0000000..a319e05 --- /dev/null +++ b/usr/include/pwd.h @@ -0,0 +1,21 @@ +#ifndef _PWD_H +#define _PWD_H + +#include <klibc/extern.h> +#include <sys/types.h> + +struct passwd { + char *pw_name; + char *pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; +}; + +__extern struct passwd *getpwuid(uid_t uid); + +__extern struct passwd *getpwnam(const char *name); + +#endif /* _PWD_H */ diff --git a/usr/include/sched.h b/usr/include/sched.h new file mode 100644 index 0000000..6874855 --- /dev/null +++ b/usr/include/sched.h @@ -0,0 +1,39 @@ +/* + * sched.h + */ + +#ifndef _SCHED_H +#define _SCHED_H + +#include <klibc/extern.h> +#include <sys/types.h> + +/* linux/sched.h is unusable; put the declarations we need here... */ + +#define SCHED_OTHER 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 + +struct sched_param { + int sched_priority; +}; + +__extern int sched_setscheduler(pid_t, int, const struct sched_param *); +__extern int sched_setaffinity(pid_t, unsigned int, unsigned long *); +__extern int sched_getaffinity(pid_t, unsigned int, unsigned long *); +__extern int sched_yield(void); + +/* Raw interfaces to clone(2); only actually usable for non-VM-cloning */ +#ifdef __ia64__ +__extern pid_t __clone2(int, void *, void *); +static __inline__ pid_t __clone(int _f, void *_sp) +{ + /* If this is used with _sp != 0 it will have the effect of the sp + and rsp growing away from a single point in opposite directions. */ + return __clone2(_f, _sp, _sp); +} +#else +__extern pid_t __clone(int, void *); +#endif + +#endif /* _SCHED_H */ diff --git a/usr/include/setjmp.h b/usr/include/setjmp.h new file mode 100644 index 0000000..5916cd8 --- /dev/null +++ b/usr/include/setjmp.h @@ -0,0 +1,48 @@ +/* + * setjmp.h + */ + +#ifndef _SETJMP_H +#define _SETJMP_H + +#include <klibc/extern.h> +#include <klibc/compiler.h> +#include <stddef.h> +#include <signal.h> + +#include <klibc/archsetjmp.h> + +__extern int setjmp(jmp_buf); +__extern __noreturn longjmp(jmp_buf, int); + +/* + Whose bright idea was it to add unrelated functionality to just about + the only function in the standard C library (setjmp) which cannot be + wrapped by an ordinary function wrapper? Anyway, the damage is done, + and therefore, this wrapper *must* be inline. However, gcc will + complain if this is an inline function for unknown reason, and + therefore sigsetjmp() needs to be a macro. +*/ + +struct __sigjmp_buf { + jmp_buf __jmpbuf; + sigset_t __sigs; + unsigned char __sigs_saved; +}; + +typedef struct __sigjmp_buf sigjmp_buf[1]; + +#define sigsetjmp(__env, __save) \ +({ \ + struct __sigjmp_buf *__e = (__env); \ + if (__save) { \ + sigprocmask(0, NULL, &__e->__sigs); \ + __e->__sigs_saved = 1; \ + } else \ + __e->__sigs_saved = 0; \ + setjmp(__e->__jmpbuf); \ +}) + +__extern __noreturn siglongjmp(sigjmp_buf, int); + +#endif /* _SETJMP_H */ diff --git a/usr/include/signal.h b/usr/include/signal.h new file mode 100644 index 0000000..a513282 --- /dev/null +++ b/usr/include/signal.h @@ -0,0 +1,103 @@ +/* + * signal.h + */ + +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include <klibc/compiler.h> +#include <klibc/extern.h> +#include <string.h> /* For memset() */ +#include <limits.h> /* For LONG_BIT */ +#include <sys/types.h> + +#include <klibc/archsignal.h> /* Includes <asm/signal.h> if appropriate */ + +/* glibc seems to use sig_atomic_t as "int" pretty much on all architectures. + Do the same, but allow the architecture to override. */ +#ifndef _KLIBC_HAS_ARCH_SIG_ATOMIC_T +typedef int sig_atomic_t; +#endif + +/* Some architectures don't define these */ +#ifndef SA_RESETHAND +# define SA_RESETHAND SA_ONESHOT +#endif +#ifndef SA_NODEFER +# define SA_NODEFER SA_NOMASK +#endif +/* Some architectures define NSIG and not _NSIG or vice versa */ +#ifndef NSIG +# define NSIG _NSIG +#endif +#ifndef _NSIG +# define _NSIG NSIG +#endif + +/* If we don't have any real-time signals available to userspace, + hide them all */ +#if SIGRTMAX <= SIGRTMIN +# undef SIGRTMIN +# undef SIGRTMAX +#endif + +/* The kernel header files are inconsistent whether or not + SIGRTMAX is inclusive or exclusive. POSIX seems to state that + it's inclusive, however. */ +#if SIGRTMAX >= _NSIG +# undef SIGRTMAX +# define SIGRTMAX (_NSIG-1) +#endif + +__extern const char *const sys_siglist[_NSIG]; +__extern const char *const sys_sigabbrev[_NSIG]; + +/* This assumes sigset_t is either an unsigned long or an array of such, + and that _NSIG_BPW in the kernel is always LONG_BIT */ + +static __inline__ int sigemptyset(sigset_t * __set) +{ + memset(__set, 0, sizeof *__set); + return 0; +} +static __inline__ int sigfillset(sigset_t * __set) +{ + memset(__set, ~0, sizeof *__set); + return 0; +} +static __inline__ int sigaddset(sigset_t * __set, int __signum) +{ + unsigned long *__lset = (unsigned long *)__set; + __signum--; /* Signal 0 is not in the set */ + __lset[__signum / LONG_BIT] |= 1UL << (__signum % LONG_BIT); + return 0; +} +static __inline__ int sigdelset(sigset_t * __set, int __signum) +{ + unsigned long *__lset = (unsigned long *)__set; + __signum--; /* Signal 0 is not in the set */ + __lset[__signum / LONG_BIT] &= ~(1UL << (__signum % LONG_BIT)); + return 0; +} +static __inline__ int sigismember(sigset_t * __set, int __signum) +{ + unsigned long *__lset = (unsigned long *)__set; + __signum--; /* Signal 0 is not in the set */ + return (int)((__lset[__signum / LONG_BIT] >> (__signum % LONG_BIT)) & + 1); +} + +__extern __sighandler_t __signal(int, __sighandler_t, int); +#ifndef signal +__extern __sighandler_t signal(int, __sighandler_t); +#endif +__extern __sighandler_t sysv_signal(int, __sighandler_t); +__extern __sighandler_t bsd_signal(int, __sighandler_t); +__extern int sigaction(int, const struct sigaction *, struct sigaction *); +__extern int sigprocmask(int, const sigset_t *, sigset_t *); +__extern int sigpending(sigset_t *); +__extern int sigsuspend(const sigset_t *); +__extern int raise(int); +__extern int kill(pid_t, int); + +#endif /* _SIGNAL_H */ diff --git a/usr/include/stdarg.h b/usr/include/stdarg.h new file mode 100644 index 0000000..cc324b8 --- /dev/null +++ b/usr/include/stdarg.h @@ -0,0 +1,14 @@ +/* + * stdarg.h + * + * This is just a wrapper for the gcc one, but defines va_copy() + * even if gcc doesn't. + */ + +/* Note: the _STDARG_H macro belongs to the gcc header... */ +#include_next <stdarg.h> + +/* Older gcc considers this an extension, so it's double underbar only */ +#ifndef va_copy +#define va_copy(d,s) __va_copy(d,s) +#endif diff --git a/usr/include/stddef.h b/usr/include/stddef.h new file mode 100644 index 0000000..6811451 --- /dev/null +++ b/usr/include/stddef.h @@ -0,0 +1,54 @@ +/* + * stddef.h + */ + +#ifndef _STDDEF_H +#define _STDDEF_H + +#ifndef __KLIBC__ +# error "__KLIBC__ not defined, compiler invocation error!" +#endif + +/* + * __SIZE_TYPE__ and __PTRDIFF_TYPE__ are defined by GCC and + * many other compilers to what types the ABI expects on the + * target platform for size_t and ptrdiff_t, so we use these + * for size_t, ssize_t, ptrdiff_t definitions, if available; + * fall back to unsigned long, which is correct on ILP32 and + * LP64 platforms (Linux does not have any others) otherwise. + * + * Note: the order "long unsigned int" precisely matches GCC. + */ +#ifndef __SIZE_TYPE__ +#define __SIZE_TYPE__ long unsigned int +#endif + +#ifndef __PTRDIFF_TYPE__ +#define __PTRDIFF_TYPE__ long int +#endif + +#define _SIZE_T +typedef __SIZE_TYPE__ size_t; + +#define _PTRDIFF_T +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +#undef NULL +#ifdef __cplusplus +# define NULL 0 +#else +# define NULL ((void *)0) +#endif + +#undef offsetof +#define offsetof(t,m) ((size_t)&((t *)0)->m) + +/* + * The container_of construct: if p is a pointer to member m of + * container class c, then return a pointer to the container of which + * *p is a member. + */ +#undef container_of +#define container_of(p, c, m) ((c *)((char *)(p) - offsetof(c,m))) + +#endif /* _STDDEF_H */ diff --git a/usr/include/stdint.h b/usr/include/stdint.h new file mode 100644 index 0000000..f64f027 --- /dev/null +++ b/usr/include/stdint.h @@ -0,0 +1,116 @@ +/* + * stdint.h + */ + +#ifndef _STDINT_H +#define _STDINT_H + +#include <bitsize/stdint.h> + +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +typedef int8_t int_fast8_t; +typedef int64_t int_fast64_t; + +typedef uint8_t uint_fast8_t; +typedef uint64_t uint_fast64_t; + +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) + +#define INT8_MIN (-128) +#define INT16_MIN (-32768) +#define INT32_MIN (-2147483647-1) +#define INT64_MIN (__INT64_C(-9223372036854775807)-1) + +#define INT8_MAX (127) +#define INT16_MAX (32767) +#define INT32_MAX (2147483647) +#define INT64_MAX (__INT64_C(9223372036854775807)) + +#define UINT8_MAX (255U) +#define UINT16_MAX (65535U) +#define UINT32_MAX (4294967295U) +#define UINT64_MAX (__UINT64_C(18446744073709551615)) + +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN + +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX + +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST64_MIN INT64_MIN + +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST64_MAX INT64_MAX + +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST64_MAX UINT64_MAX + +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +#include <bitsize/stdintlimits.h> + +#endif + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) + +#define INT8_C(c) c +#define INT16_C(c) c +#define INT32_C(c) c +#define INT64_C(c) __INT64_C(c) + +#define UINT8_C(c) c ## U +#define UINT16_C(c) c ## U +#define UINT32_C(c) c ## U +#define UINT64_C(c) __UINT64_C(c) + +#define INT_LEAST8_C(c) INT8_C(c) +#define INT_LEAST16_C(c) INT16_C(c) +#define INT_LEAST32_C(c) INT32_C(c) +#define INT_LEAST64_C(c) INT64_C(c) + +#define UINT_LEAST8_C(c) UINT8_C(c) +#define UINT_LEAST16_C(c) UINT16_C(c) +#define UINT_LEAST32_C(c) UINT32_C(c) +#define UINT_LEAST64_C(c) UINT64_C(c) + +#define INT_FAST8_C(c) INT8_C(c) +#define INT_FAST64_C(c) INT64_C(c) + +#define UINT_FAST8_C(c) UINT8_C(c) +#define UINT_FAST64_C(c) UINT64_C(c) + +#define INTMAX_C(c) INT64_C(c) +#define UINTMAX_C(c) UINT64_C(c) + +#include <bitsize/stdintconst.h> + +#endif + +/* Keep the kernel from trying to define these types... */ +#define __BIT_TYPES_DEFINED__ + +#endif /* _STDINT_H */ diff --git a/usr/include/stdio.h b/usr/include/stdio.h new file mode 100644 index 0000000..521213d --- /dev/null +++ b/usr/include/stdio.h @@ -0,0 +1,156 @@ +/* + * stdio.h + */ + +#ifndef _STDIO_H +#define _STDIO_H + +#include <klibc/extern.h> +#include <klibc/sysconfig.h> +#include <klibc/seek.h> +#include <stdarg.h> +#include <stddef.h> +#include <unistd.h> + +struct _IO_file { + int _IO_fileno; /* Underlying file descriptor */ + _Bool _IO_eof; /* End of file flag */ + _Bool _IO_error; /* Error flag */ +}; +typedef struct _IO_file FILE; + +#ifndef EOF +# define EOF (-1) +#endif + +#ifndef BUFSIZ +# define BUFSIZ _KLIBC_BUFSIZ +#endif + +enum _IO_bufmode { + _IONBF, + _IOLBF, + _IOFBF +}; +#define _IONBF _IONBF +#define _IOLBF _IOLBF +#define _IOFBF _IOFBF + +/* + * Convert between a FILE * and a file descriptor. + */ +__extern FILE *stdin, *stdout, *stderr; + +__extern FILE *fopen(const char *, const char *); +__extern FILE *fdopen(int, const char *); +__extern int fclose(FILE *); +__extern int fseek(FILE *, off_t, int); +#define fseeko fseek +__extern void rewind(FILE *); +__extern int fputs(const char *, FILE *); +__extern int fputs_unlocked(const char *, FILE *); +__extern int puts(const char *); +__extern int fputc(int, FILE *); +__extern int fputc_unlocked(int, FILE *); +#define putc(c,f) fputc((c),(f)) +#define putc_unlocked(c,f) putc((c),(f)) +#define putchar(c) fputc((c),stdout) +#define putchar_unlocked(c) putchar(c) + +__extern int fgetc(FILE *); +__extern int fgetc_unlocked(FILE *); +__extern char *fgets(char *, int, FILE *); +__extern char *fgets_unlocked(char *, int, FILE *); +#define getc(f) fgetc(f) +__extern int getc_unlocked(FILE *); +#define getc_unlocked(f) fgetc(f) +#define getchar() fgetc(stdin) +#define getchar_unlocked() getchar() +__extern int ungetc(int, FILE *); + +__extern int printf(const char *, ...); +__extern int vprintf(const char *, va_list); +__extern int fprintf(FILE *, const char *, ...); +__extern int vfprintf(FILE *, const char *, va_list); +__extern int sprintf(char *, const char *, ...); +__extern int vsprintf(char *, const char *, va_list); +__extern int snprintf(char *, size_t n, const char *, ...); +__extern int vsnprintf(char *, size_t n, const char *, va_list); +__extern int asprintf(char **, const char *, ...); +__extern int vasprintf(char **, const char *, va_list); + +__extern int sscanf(const char *, const char *, ...); +__extern int vsscanf(const char *, const char *, va_list); + +__extern void perror(const char *); + +__extern int rename(const char *, const char *); +__extern int renameat(int, const char *, int, const char *); +__extern int renameat2(int, const char *, int, const char *, unsigned int); + +__extern int remove(const char *); + +__extern size_t _fread(void *, size_t, FILE *); +__extern size_t _fwrite(const void *, size_t, FILE *); +__extern int fflush(FILE *); +__extern int fflush_unlocked(FILE *); + +__extern size_t fread(void *, size_t, size_t, FILE *); +__extern size_t fread_unlocked(void *, size_t, size_t, FILE *); +__extern size_t fwrite(const void *, size_t, size_t, FILE *); +__extern size_t fwrite_unlocked(const void *, size_t, size_t, FILE *); + +__extern off_t ftell(FILE *__f); +#define ftello ftell + +__extern int ferror(FILE * ); +__extern int ferror_unlocked(FILE * ); +__extern int feof(FILE *); +__extern int feof_unlocked(FILE *); +__extern int fileno(FILE *); +__extern int fileno_unlocked(FILE *); +__extern void clearerr(FILE *); +__extern void clearerr_unlocked(FILE *); + +#ifndef __NO_STDIO_INLINES +__extern_inline size_t +fread(void *__p, size_t __s, size_t __n, FILE * __f) +{ + return _fread(__p, __s * __n, __f) / __s; +} +#define fread_unlocked(p, s, n, f) fread((p), (s), (n), (f)) + +__extern_inline size_t +fwrite(const void *__p, size_t __s, size_t __n, FILE * __f) +{ + return _fwrite(__p, __s * __n, __f) / __s; +} +#define fwrite_unlocked(p, s, n, f) fwrite((p), (s), (n), (f)) + +__extern_inline int fileno(FILE *__f) +{ + return __f->_IO_fileno; +} +#define fileno_unlocked(f) fileno(f) + +__extern_inline int ferror(FILE *__f) +{ + return __f->_IO_error; +} +#define ferror_unlocked(f) ferror(f) + +__extern_inline int feof(FILE *__f) +{ + return __f->_IO_eof; +} +#define feof_unlocked(f) feof(f) + +__extern_inline void clearerr(FILE *__f) +{ + __f->_IO_error = 0; + __f->_IO_eof = 0; +} +#define clearerr_unlocked(f) clearerr(f) +#endif + +#endif /* _STDIO_H */ diff --git a/usr/include/stdlib.h b/usr/include/stdlib.h new file mode 100644 index 0000000..43bb8a3 --- /dev/null +++ b/usr/include/stdlib.h @@ -0,0 +1,104 @@ +/* + * stdlib.h + */ + +#ifndef _STDLIB_H +#define _STDLIB_H + +#include <klibc/extern.h> +#include <klibc/compiler.h> +#include <stddef.h> + +#include <malloc.h> +#include <fcntl.h> + +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 + +__extern __noreturn abort(void); +static __inline__ int abs(int __n) +{ + return (__n < 0) ? -__n : __n; +} +__extern int system(const char *string); +__extern int atexit(void (*)(void)); +__extern int on_exit(void (*)(int, void *), void *); +__extern int atoi(const char *); +__extern long atol(const char *); +__extern long long atoll(const char *); +__extern __noreturn exit(int); +__extern __noreturn _exit(int); +#define _Exit _exit +static __inline__ long labs(long __n) +{ + return (__n < 0L) ? -__n : __n; +} + +static __inline__ long long llabs(long long __n) +{ + return (__n < 0LL) ? -__n : __n; +} + +__extern long strtol(const char *, char **, int); +__extern long long strtoll(const char *, char **, int); +__extern unsigned long strtoul(const char *, char **, int); +__extern unsigned long long strtoull(const char *, char **, int); + +__extern char *getenv(const char *); +__extern int putenv(const char *); +__extern int setenv(const char *, const char *, int); +__extern int unsetenv(const char *); +__extern int clearenv(void); + +typedef int (*__comparefunc_t) (const void *, const void *); +__extern void *bsearch(const void *, const void *, size_t, size_t, + __comparefunc_t); +__extern void qsort(void *, size_t, size_t, __comparefunc_t); + +__extern long jrand48(unsigned short[3]); +__extern long mrand48(void); +__extern long nrand48(unsigned short[3]); +__extern long lrand48(void); +__extern unsigned short *seed48(const unsigned short[3]); +__extern void srand48(long); + +#define RAND_MAX 0x7fffffff +static __inline__ int rand(void) +{ + return (int)lrand48(); +} +static __inline__ void srand(unsigned int __s) +{ + srand48(__s); +} +static __inline__ long random(void) +{ + return lrand48(); +} +static __inline__ void srandom(unsigned int __s) +{ + srand48(__s); +} + +/* Basic PTY functions. These only work if devpts is mounted! */ + +__extern int unlockpt(int); +__extern char *ptsname(int); + +static __inline__ int posix_openpt(int __mode) +{ + __extern int open(const char *, int, ...); + + __mode &= ~(O_CREAT | O_TMPFILE); + return open("/dev/ptmx", __mode); +} + +static __inline__ int grantpt(int __fd) +{ + (void)__fd; + return 0; /* devpts does this all for us! */ +} + +__extern char *realpath(const char *, char *); + +#endif /* _STDLIB_H */ diff --git a/usr/include/string.h b/usr/include/string.h new file mode 100644 index 0000000..0c8c046 --- /dev/null +++ b/usr/include/string.h @@ -0,0 +1,49 @@ +/* + * string.h + */ + +#ifndef _STRING_H +#define _STRING_H + +#include <klibc/extern.h> +#include <stddef.h> + +__extern void *memccpy(void *, const void *, int, size_t); +__extern void *memchr(const void *, int, size_t); +__extern void *memrchr(const void *, int, size_t); +__extern int memcmp(const void *, const void *, size_t); +__extern void *memcpy(void *, const void *, size_t); +__extern void *memmove(void *, const void *, size_t); +__extern void *memset(void *, int, size_t); +__extern void *memmem(const void *, size_t, const void *, size_t); +__extern void memswap(void *, void *, size_t); +__extern void bzero(void *, size_t); +__extern int strcasecmp(const char *, const char *); +__extern int strncasecmp(const char *, const char *, size_t); +__extern char *strcat(char *, const char *); +__extern char *strchr(const char *, int); +__extern char *index(const char *, int); +__extern char *strrchr(const char *, int); +__extern char *rindex(const char *, int); +__extern int strcmp(const char *, const char *); +__extern char *strcpy(char *, const char *); +__extern size_t strcspn(const char *, const char *); +__extern char *strdup(const char *); +__extern char *strndup(const char *, size_t); +__extern char *strerror(int); +__extern char *strsignal(int); +__extern size_t strlen(const char *); +__extern size_t strnlen(const char *, size_t); +__extern char *strncat(char *, const char *, size_t); +__extern size_t strlcat(char *, const char *, size_t); +__extern int strncmp(const char *, const char *, size_t); +__extern char *strncpy(char *, const char *, size_t); +__extern size_t strlcpy(char *, const char *, size_t); +__extern char *strpbrk(const char *, const char *); +__extern char *strsep(char **, const char *); +__extern size_t strspn(const char *, const char *); +__extern char *strstr(const char *, const char *); +__extern char *strtok(char *, const char *); +__extern char *strtok_r(char *, const char *, char **); + +#endif /* _STRING_H */ diff --git a/usr/include/sys/auxv.h b/usr/include/sys/auxv.h new file mode 100644 index 0000000..fc98ed4 --- /dev/null +++ b/usr/include/sys/auxv.h @@ -0,0 +1,17 @@ +#ifndef _SYS_AUXV_H +#define _SYS_AUXV_H + +#include <klibc/compiler.h> +#include <klibc/extern.h> +#include <elf.h> + +#define _AUXVAL_MAX AT_SYSINFO_EHDR + +__extern unsigned long __auxval[_AUXVAL_MAX]; + +__static_inline unsigned long getauxval(unsigned long __t) +{ + return (__t >= _AUXVAL_MAX) ? 0 : __auxval[__t]; +} + +#endif /* _SYS_AUXV_H */ diff --git a/usr/include/sys/capability.h b/usr/include/sys/capability.h new file mode 100644 index 0000000..8b92984 --- /dev/null +++ b/usr/include/sys/capability.h @@ -0,0 +1,11 @@ +#ifndef _SYS_CAPABILITY_H +#define _SYS_CAPABILITY_H + +#include <sys/types.h> +#include <klibc/extern.h> +#include <linux/capability.h> + +__extern int capget(cap_user_header_t, cap_user_data_t); +__extern int capset(cap_user_header_t, const cap_user_data_t); + +#endif /* _SYS_CAPABILITY_H */ diff --git a/usr/include/sys/dirent.h b/usr/include/sys/dirent.h new file mode 100644 index 0000000..18b7a33 --- /dev/null +++ b/usr/include/sys/dirent.h @@ -0,0 +1,32 @@ +/* + * sys/dirent.h + */ + +#ifndef _SYS_DIRENT_H +#define _SYS_DIRENT_H + +#include <stdint.h> + +/* The kernel calls this struct dirent64 */ +struct dirent { + uint64_t d_ino; + int64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +/* File types to use for d_type */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +__extern int getdents(unsigned int, struct dirent *, unsigned int); + +#endif /* _SYS_DIRENT_H */ diff --git a/usr/include/sys/elf32.h b/usr/include/sys/elf32.h new file mode 100644 index 0000000..6da2ddb --- /dev/null +++ b/usr/include/sys/elf32.h @@ -0,0 +1,117 @@ +/* + * sys/elf32.h + */ + +#ifndef _SYS_ELF32_H +#define _SYS_ELF32_H + +#include <sys/elfcommon.h> + +/* ELF standard typedefs (yet more proof that <stdint.h> was way overdue) */ +typedef uint16_t Elf32_Half; +typedef int16_t Elf32_SHalf; +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; + +typedef uint32_t Elf32_Off; +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Section; + +/* Dynamic header */ + +typedef struct elf32_dyn { + Elf32_Sword d_tag; + union { + Elf32_Sword d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +/* Relocations */ + +#define ELF32_R_SYM(x) ((x) >> 8) +#define ELF32_R_TYPE(x) ((x) & 0xff) + +typedef struct elf32_rel { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct elf32_rela { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +/* Symbol */ + +typedef struct elf32_sym { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +/* Main file header */ + +typedef struct elf32_hdr { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + 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; + +/* Program header */ + +typedef struct elf32_phdr { + 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; + +/* Section header */ + +typedef struct elf32_shdr { + 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; + +/* Note header */ +typedef struct elf32_note { + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ +} Elf32_Nhdr; + +/* How to extract and insert information held in the st_info field. */ +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) + +#endif /* _SYS_ELF32_H */ diff --git a/usr/include/sys/elf64.h b/usr/include/sys/elf64.h new file mode 100644 index 0000000..877b7cd --- /dev/null +++ b/usr/include/sys/elf64.h @@ -0,0 +1,117 @@ +/* + * sys/elf64.h + */ + +#ifndef _SYS_ELF64_H +#define _SYS_ELF64_H + +#include <sys/elfcommon.h> + +/* ELF standard typedefs (yet more proof that <stdint.h> was way overdue) */ +typedef uint16_t Elf64_Half; +typedef int16_t Elf64_SHalf; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +typedef uint64_t Elf64_Off; +typedef uint64_t Elf64_Addr; +typedef uint16_t Elf64_Section; + +/* Dynamic header */ + +typedef struct elf64_dyn { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +/* Relocations */ + +#define ELF64_R_SYM(x) ((x) >> 32) +#define ELF64_R_TYPE(x) ((x) & 0xffffffff) + +typedef struct elf64_rel { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + +typedef struct elf64_rela { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + +/* Symbol */ + +typedef struct elf64_sym { + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +/* Main file header */ + +typedef struct elf64_hdr { + unsigned char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + 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; + +/* Program header */ + +typedef struct elf64_phdr { + 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; + +/* Section header */ + +typedef struct elf64_shdr { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +/* Note header */ +typedef struct elf64_note { + Elf64_Word n_namesz; /* Name size */ + Elf64_Word n_descsz; /* Content size */ + Elf64_Word n_type; /* Content type */ +} Elf64_Nhdr; + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) + +#endif /* _SYS_ELF64_H */ diff --git a/usr/include/sys/elfcommon.h b/usr/include/sys/elfcommon.h new file mode 100644 index 0000000..603b0ce --- /dev/null +++ b/usr/include/sys/elfcommon.h @@ -0,0 +1,202 @@ +/* + * sys/elfcommon.h + */ + +#ifndef _SYS_ELFCOMMON_H +#define _SYS_ELFCOMMON_H + +#include <stdint.h> + +/* Segment types */ +#define PT_NULL 0 +#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_LOOS 0x60000000 +#define PT_HIOS 0x6fffffff +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff +#define PT_GNU_EH_FRAME 0x6474e550 /* Extension, eh? */ + +/* ELF file types */ +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + +/* ELF machine types */ +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_486 6 /* Not used in Linux at least */ +#define EM_860 7 +#define EM_MIPS 8 /* R3k, bigendian(?) */ +#define EM_MIPS_RS4_BE 10 /* R4k BE */ +#define EM_PARISC 15 +#define EM_SPARC32PLUS 18 +#define EM_PPC 20 +#define EM_PPC64 21 +#define EM_S390 22 +#define EM_SH 42 +#define EM_SPARCV9 43 /* v9 = SPARC64 */ +#define EM_H8_300H 47 +#define EM_H8S 48 +#define EM_IA_64 50 /* Itanic */ +#define EM_X86_64 62 +#define EM_CRIS 76 +#define EM_V850 87 +#define EM_ALPHA 0x9026 /* Interrim Alpha that stuck around */ +#define EM_CYGNUS_V850 0x9080 /* Old v850 ID used by Cygnus */ +#define EM_S390_OLD 0xA390 /* Obsolete interrim value for S/390 */ + +/* Dynamic type values */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff + +/* Auxilliary table entries */ +#define AT_NULL 0 /* end of vector */ +#define AT_IGNORE 1 /* entry should be ignored */ +#define AT_EXECFD 2 /* file descriptor of program */ +#define AT_PHDR 3 /* program headers for program */ +#define AT_PHENT 4 /* size of program header entry */ +#define AT_PHNUM 5 /* number of program headers */ +#define AT_PAGESZ 6 /* system page size */ +#define AT_BASE 7 /* base address of interpreter */ +#define AT_FLAGS 8 /* flags */ +#define AT_ENTRY 9 /* entry point of program */ +#define AT_NOTELF 10 /* program is not ELF */ +#define AT_UID 11 /* real uid */ +#define AT_EUID 12 /* effective uid */ +#define AT_GID 13 /* real gid */ +#define AT_EGID 14 /* effective gid */ +#define AT_PLATFORM 15 /* string identifying CPU for optimizations */ +#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define AT_CLKTCK 17 /* frequency at which times() increments */ +/* 18..22 = ? */ +#define AT_SECURE 23 /* secure mode boolean */ +#define AT_SYSINFO 32 /* vdso entry point address */ +#define AT_SYSINFO_EHDR 33 /* vdso header address */ + +/* Program header permission flags */ +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 + +/* Section header types */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_NUM 12 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +/* Section header flags */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + +/* Special section numbers */ +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +/* End of a chain. */ +#define STN_UNDEF 0 + +/* Lenght of magic at the start of a file */ +#define EI_NIDENT 16 + +/* Magic number constants... */ +#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_OSABI 7 +#define EI_PAD 8 + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define EV_NONE 0 /* e_version, EI_VERSION */ +#define EV_CURRENT 1 +#define EV_NUM 2 + +#define ELFOSABI_NONE 0 +#define ELFOSABI_LINUX 3 + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +#endif /* _SYS_ELFCOMMON_H */ diff --git a/usr/include/sys/file.h b/usr/include/sys/file.h new file mode 100644 index 0000000..7b580f3 --- /dev/null +++ b/usr/include/sys/file.h @@ -0,0 +1,9 @@ +#ifndef _SYS_FILE_H +#define _SYS_FILE_H + +/* LOCK_ definitions */ +#include <fcntl.h> + +__extern int flock(int, int); + +#endif /* _SYS_FILE_H */ diff --git a/usr/include/sys/fsuid.h b/usr/include/sys/fsuid.h new file mode 100644 index 0000000..d64e28b --- /dev/null +++ b/usr/include/sys/fsuid.h @@ -0,0 +1,14 @@ +/* + * sys/fsuid.h + */ + +#ifndef _SYS_FSUID_H +#define _SYS_FSUID_H + +#include <klibc/extern.h> +#include <sys/types.h> + +__extern int setfsuid(uid_t); +__extern int setfsgid(gid_t); + +#endif /* _SYS_FSUID_H */ diff --git a/usr/include/sys/inotify.h b/usr/include/sys/inotify.h new file mode 100644 index 0000000..e08cb05 --- /dev/null +++ b/usr/include/sys/inotify.h @@ -0,0 +1,16 @@ +/* + * sys/inotify.h + */ + +#ifndef _SYS_INOTIFY_H +#define _SYS_INOTIFY_H + +#include <sys/types.h> +#include <linux/inotify.h> +#include <klibc/extern.h> + +__extern int inotify_init(void); +__extern int inotify_add_watch(int, const char *, __u32); +__extern int inotify_rm_watch(int, __u32); + +#endif /* _SYS_INOTIFY_H */ diff --git a/usr/include/sys/ioctl.h b/usr/include/sys/ioctl.h new file mode 100644 index 0000000..7b0b687 --- /dev/null +++ b/usr/include/sys/ioctl.h @@ -0,0 +1,18 @@ +/* + * sys/ioctl.h + */ + +#ifndef _SYS_IOCTL_H +#define _SYS_IOCTL_H + +#include <sys/types.h> +#include <klibc/extern.h> +#include <linux/ioctl.h> +#include <asm/ioctls.h> + +/* the SIOCxxx I/O are hidden */ +#include <linux/sockios.h> + +__extern int ioctl(int, int, void *); + +#endif /* _SYS_IOCTL_H */ diff --git a/usr/include/sys/klog.h b/usr/include/sys/klog.h new file mode 100644 index 0000000..02c7217 --- /dev/null +++ b/usr/include/sys/klog.h @@ -0,0 +1,24 @@ +/* + * sys/klog.h + */ + +#ifndef _SYS_KLOG_H +#define _SYS_KLOG_H + +#include <klibc/extern.h> + +#define KLOG_CLOSE 0 +#define KLOG_OPEN 1 +#define KLOG_READ 2 +#define KLOG_READ_ALL 3 +#define KLOG_READ_CLEAR 4 +#define KLOG_CLEAR 5 +#define KLOG_DISABLE 6 +#define KLOG_ENABLE 7 +#define KLOG_SETLEVEL 8 +#define KLOG_UNREADSIZE 9 +#define KLOG_WRITE 10 + +__extern int klogctl(int, char *, int); + +#endif /* _SYS_KLOG_H */ diff --git a/usr/include/sys/md.h b/usr/include/sys/md.h new file mode 100644 index 0000000..63be3d8 --- /dev/null +++ b/usr/include/sys/md.h @@ -0,0 +1,34 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * sys/md.h + * + * Defines for the Linux md functionality. Some of this stuff is + * userspace-visible but lives in md_k.h, which is user-space unsafe. + * Sigh. + */ + +#ifndef _SYS_MD_H +#define _SYS_MD_H + +#include <sys/types.h> + +#define LEVEL_MULTIPATH (-4) +#define LEVEL_LINEAR (-1) +#define LEVEL_FAULTY (-5) +#define MAX_MD_DEVS 256 /* Max number of md dev */ + +#include <linux/raid/md_u.h> +#include <linux/raid/md_p.h> + +#endif /* _SYS_MD_H */ diff --git a/usr/include/sys/mman.h b/usr/include/sys/mman.h new file mode 100644 index 0000000..3fd626e --- /dev/null +++ b/usr/include/sys/mman.h @@ -0,0 +1,26 @@ +/* + * sys/mman.h + */ + +#ifndef _SYS_MMAN_H +#define _SYS_MMAN_H + +#include <klibc/extern.h> +#include <sys/types.h> +#include <linux/mman.h> + +#define MAP_FAILED ((void *)-1) + +__extern void *mmap(void *, size_t, int, int, int, off_t); +__extern int munmap(void *, size_t); +__extern void *mremap(void *, size_t, size_t, unsigned long); +__extern int shm_open(const char *, int, mode_t); +__extern int shm_unlink(const char *); +__extern int msync(const void *, size_t, int); +__extern int mprotect(const void *, size_t, int); +__extern int mlockall(int); +__extern int munlockall(void); +__extern int mlock(const void *, size_t); +__extern int munlock(const void *, size_t); + +#endif /* _SYS_MMAN_H */ diff --git a/usr/include/sys/mount.h b/usr/include/sys/mount.h new file mode 100644 index 0000000..9d6b0ee --- /dev/null +++ b/usr/include/sys/mount.h @@ -0,0 +1,71 @@ +/* + * sys/mount.h + */ + +#ifndef _SYS_MOUNT_H +#define _SYS_MOUNT_H + +#include <klibc/extern.h> +#include <sys/ioctl.h> + +/* + * These are the fs-independent mount-flags: up to 32 flags are supported + */ +#define MS_RDONLY 0x0001 /* Mount read-only */ +#define MS_NOSUID 0x0002 /* Ignore suid and sgid bits */ +#define MS_NODEV 0x0004 /* Disallow access to device special files */ +#define MS_NOEXEC 0x0008 /* Disallow program execution */ +#define MS_SYNCHRONOUS 0x0010 /* Writes are synced at once */ +#define MS_REMOUNT 0x0020 /* Alter flags of a mounted FS */ +#define MS_MANDLOCK 0x0040 /* Allow mandatory locks on an FS */ +#define MS_DIRSYNC 0x0080 /* Directory modifications are synchronous */ +#define MS_NOATIME 0x0400 /* Do not update access times. */ +#define MS_NODIRATIME 0x0800 /* Do not update directory access times */ +#define MS_BIND 0x1000 +#define MS_MOVE 0x2000 +#define MS_REC 0x4000 +#define MS_VERBOSE 0x8000 +#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */ +#define MS_ONE_SECOND (1<<17) /* fs has 1 sec a/m/ctime resolution */ +#define MS_ACTIVE (1<<30) +#define MS_NOUSER (1<<31) + +/* + * Superblock flags that can be altered by MS_REMOUNT + */ +#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|MS_NODIRATIME) + +/* + * Old magic mount flag and mask + */ +#define MS_MGC_VAL 0xC0ED0000 +#define MS_MGC_MSK 0xffff0000 + +/* + * umount2() flags + */ +#define MNT_FORCE 1 /* Forcibly unmount */ +#define MNT_DETACH 2 /* Detach from tree only */ +#define MNT_EXPIRE 4 /* Mark for expiry */ + +/* + * Block device ioctls + */ +#define BLKROSET _IO(0x12, 93) /* Set device read-only (0 = read-write). */ +#define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */ +#define BLKRRPART _IO(0x12, 95) /* Re-read partition table. */ +#define BLKGETSIZE _IO(0x12, 96) /* Return device size. */ +#define BLKFLSBUF _IO(0x12, 97) /* Flush buffer cache. */ +#define BLKRASET _IO(0x12, 98) /* Set read ahead for block device. */ +#define BLKRAGET _IO(0x12, 99) /* Get current read ahead setting. */ + +/* + * Prototypes + */ +__extern int mount(const char *, const char *, + const char *, unsigned long, const void *); +__extern int umount(const char *); +__extern int umount2(const char *, int); +__extern int pivot_root(const char *, const char *); + +#endif /* _SYS_MOUNT_H */ diff --git a/usr/include/sys/param.h b/usr/include/sys/param.h new file mode 100644 index 0000000..fb3b2e4 --- /dev/null +++ b/usr/include/sys/param.h @@ -0,0 +1,11 @@ +/* + * sys/param.h + */ + +#ifndef _SYS_PARAM_H +#define _SYS_PARAM_H + +#include <limits.h> +#include <linux/param.h> + +#endif /* _SYS_PARAM_H */ diff --git a/usr/include/sys/poll.h b/usr/include/sys/poll.h new file mode 100644 index 0000000..f0c60c2 --- /dev/null +++ b/usr/include/sys/poll.h @@ -0,0 +1,20 @@ +/* + * poll.h + */ + +#ifndef _POLL_H +#define _POLL_H + +#include <klibc/extern.h> +#include <sys/time.h> +#include <signal.h> +#include <linux/poll.h> + +/* POSIX specifies "int" for the timeout, Linux seems to use long... */ + +typedef unsigned int nfds_t; +__extern int poll(struct pollfd *, nfds_t, long); +__extern int ppoll(struct pollfd *, nfds_t, struct timespec *, + const sigset_t *); + +#endif /* _POLL_H */ diff --git a/usr/include/sys/prctl.h b/usr/include/sys/prctl.h new file mode 100644 index 0000000..b0f41f5 --- /dev/null +++ b/usr/include/sys/prctl.h @@ -0,0 +1,11 @@ +#ifndef _SYS_PRCTL_H +#define _SYS_PRCTL_H + +#include <sys/types.h> +#include <klibc/extern.h> +#include <linux/prctl.h> + +/* glibc has this as a varadic function, so join the club... */ +__extern int prctl(int, ...); + +#endif /* _SYS_PRCTL_H */ diff --git a/usr/include/sys/reboot.h b/usr/include/sys/reboot.h new file mode 100644 index 0000000..b46a1c5 --- /dev/null +++ b/usr/include/sys/reboot.h @@ -0,0 +1,25 @@ +/* + * sys/reboot.h + */ + +#ifndef _SYS_REBOOT_H +#define _SYS_REBOOT_H + +#include <klibc/extern.h> +#include <linux/reboot.h> + +/* glibc names these constants differently; allow both versions */ + +#define RB_AUTOBOOT LINUX_REBOOT_CMD_RESTART +#define RB_HALT_SYSTEM LINUX_REBOOT_CMD_HALT +#define RB_ENABLE_CAD LINUX_REBOOT_CMD_CAD_ON +#define RB_DISABLE_CAD LINUX_REBOOT_CMD_CAD_OFF +#define RB_POWER_OFF LINUX_REBOOT_CMD_POWER_OFF + +/* two-arguments version of reboot */ +__extern int reboot(int, void *); + +/* Native four-argument system call */ +__extern int __reboot(int, int, int, void *); + +#endif /* _SYS_REBOOT_H */ diff --git a/usr/include/sys/resource.h b/usr/include/sys/resource.h new file mode 100644 index 0000000..e43a69d --- /dev/null +++ b/usr/include/sys/resource.h @@ -0,0 +1,48 @@ +/* + * sys/resource.h + */ + +#ifndef _SYS_RESOURCE_H +#define _SYS_RESOURCE_H + +#include <klibc/extern.h> +#include <sys/types.h> +#include <sys/time.h> + +#define RUSAGE_SELF 0 +#define RUSAGE_CHILDREN (-1) +#define RUSAGE_BOTH (-2) +#define RUSAGE_THREAD 1 + +struct rusage { + struct timeval_old ru_utime; /* user time used */ + struct timeval_old ru_stime; /* system time used */ + __kernel_long_t ru_maxrss; /* maximum resident set size */ + __kernel_long_t ru_ixrss; /* integral shared memory size */ + __kernel_long_t ru_idrss; /* integral unshared data size */ + __kernel_long_t ru_isrss; /* integral unshared stack size */ + __kernel_long_t ru_minflt; /* page reclaims */ + __kernel_long_t ru_majflt; /* page faults */ + __kernel_long_t ru_nswap; /* swaps */ + __kernel_long_t ru_inblock; /* block input operations */ + __kernel_long_t ru_oublock; /* block output operations */ + __kernel_long_t ru_msgsnd; /* messages sent */ + __kernel_long_t ru_msgrcv; /* messages received */ + __kernel_long_t ru_nsignals; /* signals received */ + __kernel_long_t ru_nvcsw; /* voluntary context switches */ + __kernel_long_t ru_nivcsw; /* involuntary " */ +}; + +#define PRIO_MIN (-20) +#define PRIO_MAX 20 + +#define PRIO_PROCESS 0 +#define PRIO_PGRP 1 +#define PRIO_USER 2 + +__extern int getpriority(int, int); +__extern int setpriority(int, int, int); + +__extern int getrusage(int, struct rusage *); + +#endif /* _SYS_RESOURCE_H */ diff --git a/usr/include/sys/select.h b/usr/include/sys/select.h new file mode 100644 index 0000000..cc4e00e --- /dev/null +++ b/usr/include/sys/select.h @@ -0,0 +1,17 @@ +/* + * sys/select.h + */ + +#ifndef _SYS_SELECT_H +#define _SYS_SELECT_H + +#include <klibc/extern.h> +#include <sys/time.h> +#include <sys/types.h> +#include <signal.h> + +__extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +__extern int pselect(int, fd_set *, fd_set *, fd_set *, + const struct timespec *, const sigset_t *); + +#endif /* _SYS_SELECT_H */ diff --git a/usr/include/sys/sendfile.h b/usr/include/sys/sendfile.h new file mode 100644 index 0000000..a745f10 --- /dev/null +++ b/usr/include/sys/sendfile.h @@ -0,0 +1,14 @@ +/* + * sys/sendfile.h + */ + +#ifndef _SYS_SENDFILE_H +#define _SYS_SENDFILE_H + +#include <klibc/extern.h> +#include <stddef.h> +#include <sys/types.h> + +__extern ssize_t sendfile(int, int, off_t *, size_t, off_t); + +#endif /* _SYS_SENDFILE_H */ diff --git a/usr/include/sys/socket.h b/usr/include/sys/socket.h new file mode 100644 index 0000000..d0ba9eb --- /dev/null +++ b/usr/include/sys/socket.h @@ -0,0 +1,270 @@ +/* + * sys/socket.h + */ + +#ifndef _SYS_SOCKET_H +#define _SYS_SOCKET_H + +#include <sys/types.h> +#include <klibc/extern.h> +#include <klibc/compiler.h> +#include <klibc/sysconfig.h> +#include <linux/socket.h> +#include <linux/uio.h> +#include <asm/socket.h> +#if _KLIBC_HAS_ARCHSOCKET_H +#include <klibc/archsocket.h> +#endif + +/* Great job, guys! These are *architecture-specific* ABI constants, + that are hidden under #ifdef __KERNEL__... what a brilliant idea! + These are the "common" definitions; if not appropriate, override + them in <klibc/archsocket.h>. */ + +#ifndef SOCK_STREAM +# define SOCK_STREAM 1 +# define SOCK_DGRAM 2 +# define SOCK_RAW 3 +# define SOCK_RDM 4 +# define SOCK_SEQPACKET 5 +# define SOCK_PACKET 10 +# define SOCK_CLOEXEC 02000000 +# define SOCK_NONBLOCK 04000 +#endif + +/* The maximum backlock queue length. */ +#define SOMAXCONN 128 + +#ifndef AF_INET +#define AF_UNSPEC 0 +#define AF_UNIX 1 /* Unix domain sockets */ +#define AF_LOCAL 1 /* POSIX name for AF_UNIX */ +#define AF_INET 2 /* Internet IP Protocol */ +#define AF_AX25 3 /* Amateur Radio AX.25 */ +#define AF_IPX 4 /* Novell IPX */ +#define AF_APPLETALK 5 /* AppleTalk DDP */ +#define AF_NETROM 6 /* Amateur Radio NET/ROM */ +#define AF_BRIDGE 7 /* Multiprotocol bridge */ +#define AF_ATMPVC 8 /* ATM PVCs */ +#define AF_X25 9 /* Reserved for X.25 project */ +#define AF_INET6 10 /* IP version 6 */ +#define AF_ROSE 11 /* Amateur Radio X.25 PLP */ +#define AF_DECnet 12 /* Reserved for DECnet project */ +#define AF_NETBEUI 13 /* Reserved for 802.2LLC project*/ +#define AF_SECURITY 14 /* Security callback pseudo AF */ +#define AF_KEY 15 /* PF_KEY key management API */ +#define AF_NETLINK 16 +#define AF_ROUTE AF_NETLINK /* Alias to emulate 4.4BSD */ +#define AF_PACKET 17 /* Packet family */ +#define AF_ASH 18 /* Ash */ +#define AF_ECONET 19 /* Acorn Econet */ +#define AF_ATMSVC 20 /* ATM SVCs */ +#define AF_RDS 21 /* RDS sockets */ +#define AF_SNA 22 /* Linux SNA Project (nutters!) */ +#define AF_IRDA 23 /* IRDA sockets */ +#define AF_PPPOX 24 /* PPPoX sockets */ +#define AF_WANPIPE 25 /* Wanpipe API Sockets */ +#define AF_LLC 26 /* Linux LLC */ +#define AF_CAN 29 /* Controller Area Network */ +#define AF_TIPC 30 /* TIPC sockets */ +#define AF_BLUETOOTH 31 /* Bluetooth sockets */ +#define AF_IUCV 32 /* IUCV sockets */ +#define AF_RXRPC 33 /* RxRPC sockets */ +#define AF_ISDN 34 /* mISDN sockets */ +#define AF_PHONET 35 /* Phonet sockets */ +#define AF_IEEE802154 36 /* IEEE802154 sockets */ +#define AF_MAX 37 /* For now.. */ +#endif // !AF_INET + +#ifndef PF_UNSPEC +#define PF_UNSPEC AF_UNSPEC +#define PF_UNIX AF_UNIX +#define PF_LOCAL AF_LOCAL +#define PF_INET AF_INET +#define PF_AX25 AF_AX25 +#define PF_IPX AF_IPX +#define PF_APPLETALK AF_APPLETALK +#define PF_NETROM AF_NETROM +#define PF_BRIDGE AF_BRIDGE +#define PF_ATMPVC AF_ATMPVC +#define PF_X25 AF_X25 +#define PF_INET6 AF_INET6 +#define PF_ROSE AF_ROSE +#define PF_DECnet AF_DECnet +#define PF_NETBEUI AF_NETBEUI +#define PF_SECURITY AF_SECURITY +#define PF_KEY AF_KEY +#define PF_NETLINK AF_NETLINK +#define PF_ROUTE AF_ROUTE +#define PF_PACKET AF_PACKET +#define PF_ASH AF_ASH +#define PF_ECONET AF_ECONET +#define PF_ATMSVC AF_ATMSVC +#define PF_RDS AF_RDS +#define PF_SNA AF_SNA +#define PF_IRDA AF_IRDA +#define PF_PPPOX AF_PPPOX +#define PF_WANPIPE AF_WANPIPE +#define PF_LLC AF_LLC +#define PF_CAN AF_CAN +#define PF_TIPC AF_TIPC +#define PF_BLUETOOTH AF_BLUETOOTH +#define PF_IUCV AF_IUCV +#define PF_RXRPC AF_RXRPC +#define PF_ISDN AF_ISDN +#define PF_PHONET AF_PHONET +#define PF_IEEE802154 AF_IEEE802154 +#define PF_MAX AF_MAX +#endif // !PF_UNSPEC + +#ifndef MSG_OOB +#define MSG_OOB 1 +#define MSG_PEEK 2 +#define MSG_DONTROUTE 4 +#define MSG_TRYHARD 4 /* Synonym for MSG_DONTROUTE for DECnet */ +#define MSG_CTRUNC 8 +#define MSG_PROBE 0x10 /* Do not send. Only probe path f.e. for MTU */ +#define MSG_TRUNC 0x20 +#define MSG_DONTWAIT 0x40 /* Nonblocking io */ +#define MSG_EOR 0x80 /* End of record */ +#define MSG_WAITALL 0x100 /* Wait for a full request */ +#define MSG_FIN 0x200 +#define MSG_SYN 0x400 +#define MSG_CONFIRM 0x800 /* Confirm path validity */ +#define MSG_RST 0x1000 +#define MSG_ERRQUEUE 0x2000 /* Fetch message from error queue */ +#define MSG_NOSIGNAL 0x4000 /* Do not generate SIGPIPE */ +#define MSG_MORE 0x8000 /* Sender will send more */ + +#define MSG_EOF MSG_FIN + +#define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exit for file + descriptor received through + SCM_RIGHTS */ +#if defined(CONFIG_COMPAT) +#define MSG_CMSG_COMPAT 0x80000000 /* This message needs 32 bit fixups */ +#else +#define MSG_CMSG_COMPAT 0 /* We never have 32 bit fixups */ +#endif +#endif // !MSG_OOB + +/* These types is hidden under __KERNEL__ in kernel sources */ +typedef unsigned short sa_family_t; +struct sockaddr { + sa_family_t sa_family; /* address family, AF_xxx */ + char sa_data[14]; /* 14 bytes of protocol address */ +}; +typedef int socklen_t; +struct msghdr { + void *msg_name; + int msg_namelen; + struct iovec *msg_iov; + size_t msg_iovlen; + void *msg_control; + size_t msg_controllen; + unsigned msg_flags; +}; + +/* Ancillary data structures and cmsg macros are also hidden under __KERNEL__ */ +#ifndef CMSG_FIRSTHDR +/* + * POSIX 1003.1g - ancillary data object information + * Ancillary data consits of a sequence of pairs of + * (cmsghdr, cmsg_data[]) + */ + +struct cmsghdr { + __kernel_size_t cmsg_len; /* data byte count, including hdr */ + int cmsg_level; /* originating protocol */ + int cmsg_type; /* protocol-specific type */ +}; + +/* + * Ancilliary data object information MACROS + * Table 5-14 of POSIX 1003.1g + */ + +#define __CMSG_NXTHDR(ctl, len, cmsg) __cmsg_nxthdr((ctl),(len),(cmsg)) +#define CMSG_NXTHDR(mhdr, cmsg) cmsg_nxthdr((mhdr), (cmsg)) + +#define CMSG_ALIGN(len) ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) ) + +#define CMSG_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG_ALIGN(sizeof(struct cmsghdr)))) +#define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len)) +#define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) + +#define __CMSG_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr) ? \ + (struct cmsghdr *)(ctl) : \ + (struct cmsghdr *)NULL) +#define CMSG_FIRSTHDR(msg) __CMSG_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) +#define CMSG_OK(mhdr, cmsg) ((cmsg)->cmsg_len >= sizeof(struct cmsghdr) && \ + (cmsg)->cmsg_len <= (unsigned long) \ + ((mhdr)->msg_controllen - \ + ((char *)(cmsg) - (char *)(mhdr)->msg_control))) + +/* + * Get the next cmsg header + * + * PLEASE, do not touch this function. If you think, that it is + * incorrect, grep kernel sources and think about consequences + * before trying to improve it. + * + * Now it always returns valid, not truncated ancillary object + * HEADER. But caller still MUST check, that cmsg->cmsg_len is + * inside range, given by msg->msg_controllen before using + * ancillary object DATA. --ANK (980731) + */ + +static inline struct cmsghdr * __cmsg_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr *__cmsg) +{ + struct cmsghdr * __ptr; + + __ptr = (struct cmsghdr*)(((unsigned char *) __cmsg) + CMSG_ALIGN(__cmsg->cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return (struct cmsghdr *)0; + + return __ptr; +} + +static inline struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr *__cmsg) +{ + return __cmsg_nxthdr(__msg->msg_control, __msg->msg_controllen, __cmsg); +} + +/* "Socket"-level control message types: */ + +#define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */ +#define SCM_CREDENTIALS 0x02 /* rw: struct ucred */ +#define SCM_SECURITY 0x03 /* rw: security label */ + +struct ucred { + __u32 pid; + __u32 uid; + __u32 gid; +}; +#endif /* CMSG_FIRSTHDR */ + + +__extern int socket(int, int, int); +__extern int bind(int, const struct sockaddr *, int); +__extern int connect(int, const struct sockaddr *, socklen_t); +__extern int listen(int, int); +__extern int accept(int, struct sockaddr *, socklen_t *); +__extern int accept4(int, struct sockaddr *, socklen_t *, int); +__extern int getsockname(int, struct sockaddr *, socklen_t *); +__extern int getpeername(int, struct sockaddr *, socklen_t *); +__extern int socketpair(int, int, int, int *); +__extern int send(int, const void *, size_t, unsigned int); +__extern int sendto(int, const void *, size_t, int, const struct sockaddr *, + socklen_t); +__extern int recv(int, void *, size_t, unsigned int); +__extern int recvfrom(int, void *, size_t, unsigned int, struct sockaddr *, + socklen_t *); +__extern int shutdown(int, int); +__extern int setsockopt(int, int, int, const void *, socklen_t); +__extern int getsockopt(int, int, int, void *, socklen_t *); +__extern int sendmsg(int, const struct msghdr *, unsigned int); +__extern int recvmsg(int, struct msghdr *, unsigned int); + +#endif /* _SYS_SOCKET_H */ diff --git a/usr/include/sys/splice.h b/usr/include/sys/splice.h new file mode 100644 index 0000000..1fa26d9 --- /dev/null +++ b/usr/include/sys/splice.h @@ -0,0 +1,19 @@ +/* + * sys/splice.h + */ + +#ifndef _SYS_SPLICE_H +#define _SYS_SPLICE_H + +/* move pages instead of copying */ +#define SPLICE_F_MOVE 1 +/* don't block on the pipe splicing (but we may still block on the fd + we splice from/to, of course */ +#define SPLICE_F_NONBLOCK 2 +/* expect more data */ +#define SPLICE_F_MORE 4 + +__extern int splice(int, off_t *, int, off_t *, size_t, unsigned int); +__extern int tee(int, int, size_t, unsigned int); + +#endif /* _SYS_SPLICE_H */ diff --git a/usr/include/sys/stat.h b/usr/include/sys/stat.h new file mode 100644 index 0000000..861b462 --- /dev/null +++ b/usr/include/sys/stat.h @@ -0,0 +1,96 @@ +/* + * sys/stat.h + */ + +#ifndef _SYS_STAT_H +#define _SYS_STAT_H + +#include <klibc/extern.h> +#include <sys/types.h> +#include <sys/time.h> /* For struct timespec */ +#include <linux/stat.h> + +/* 2.6.21 kernels have once again hidden a bunch of stuff... */ +#ifndef S_IFMT + +#define S_IFMT 00170000 +#define S_IFSOCK 0140000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFBLK 0060000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFIFO 0010000 +#define S_ISUID 0004000 +#define S_ISGID 0002000 +#define S_ISVTX 0001000 + +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) + +#define S_IRWXU 00700 +#define S_IRUSR 00400 +#define S_IWUSR 00200 +#define S_IXUSR 00100 + +#define S_IRWXG 00070 +#define S_IRGRP 00040 +#define S_IWGRP 00020 +#define S_IXGRP 00010 + +#define S_IRWXO 00007 +#define S_IROTH 00004 +#define S_IWOTH 00002 +#define S_IXOTH 00001 + +#endif + +#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) +#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO) +#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) +#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) +#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) + +/* struct stat with 64-bit time, not used by kernel UAPI */ +struct stat { + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + unsigned int st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + off_t st_size; + int st_blksize; + off_t st_blocks; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; +}; +#define st_atime st_atim.tv_sec +#define st_mtime st_mtim.tv_sec +#define st_ctime st_ctim.tv_sec + +__extern int stat(const char *, struct stat *); +__extern int fstat(int, struct stat *); +__extern int fstatat(int, const char *, struct stat *, int); +__extern int lstat(const char *, struct stat *); +__extern int statx(int, const char *, int, unsigned int, struct statx *); +__extern mode_t umask(mode_t); +__extern int mknod(const char *, mode_t, dev_t); +__extern int mknodat(int, const char *, mode_t, dev_t); +__extern int mkfifo(const char *, mode_t); +__extern int utimensat(int, const char *, const struct timespec *, int); +__extern int fchmodat(int, const char *, mode_t, int); + +__extern_inline int mkfifo(const char *__p, mode_t __m) +{ + return mknod(__p, (__m & ~S_IFMT) | S_IFIFO, (dev_t) 0); +} + +#endif /* _SYS_STAT_H */ diff --git a/usr/include/sys/statfs.h b/usr/include/sys/statfs.h new file mode 100644 index 0000000..53b3b5e --- /dev/null +++ b/usr/include/sys/statfs.h @@ -0,0 +1 @@ +#include <sys/vfs.h> diff --git a/usr/include/sys/syscall.h b/usr/include/sys/syscall.h new file mode 100644 index 0000000..8fe0142 --- /dev/null +++ b/usr/include/sys/syscall.h @@ -0,0 +1,13 @@ +/* + * sys/syscall.h + * + * Generic system call interface macros + */ +#ifndef _SYS_SYSCALL_H +#define _SYS_SYSCALL_H + +#include <errno.h> +#include <sys/types.h> +#include <asm/unistd.h> + +#endif /* _SYS_SYSCALL_H */ diff --git a/usr/include/sys/sysconf.h b/usr/include/sys/sysconf.h new file mode 100644 index 0000000..13ababc --- /dev/null +++ b/usr/include/sys/sysconf.h @@ -0,0 +1,40 @@ +/* + * sys/sysconf.h + * + * sysconf() macros and demultiplex + * This file is included in <unistd.h> + * + * Add things here as needed, we don't really want to add things wildly. + * For things that require a lot of code, create an out-of-line function + * and put it in a .c file in the sysconf directory. + */ + +#ifndef _SYS_SYSCONF_H +#define _SYS_SYSCONF_H + +#ifndef _UNISTD_H +# include <unistd.h> +#endif +#include <errno.h> + +enum sysconf { + _SC_PAGESIZE = 1, +}; + +__extern long sysconf(int); + +__must_inline long __sysconf_inline(int __val) +{ + switch (__val) { + case _SC_PAGESIZE: + return getpagesize(); + default: + errno = EINVAL; + return -1; + } +} + +#define sysconf(x) \ + (__builtin_constant_p(x) ? __sysconf_inline(x) : sysconf(x)) + +#endif /* _SYS_SYSCONF_H */ diff --git a/usr/include/sys/sysinfo.h b/usr/include/sys/sysinfo.h new file mode 100644 index 0000000..8469ba0 --- /dev/null +++ b/usr/include/sys/sysinfo.h @@ -0,0 +1,13 @@ +/* + * sys/sysinfo.h + */ + +#ifndef _SYS_SYSINFO_H +#define _SYS_SYSINFO_H + +#include <sys/types.h> +#include <linux/kernel.h> + +extern int sysinfo(struct sysinfo *info); + +#endif /* _SYS_SYSINFO_H */ diff --git a/usr/include/sys/sysmacros.h b/usr/include/sys/sysmacros.h new file mode 100644 index 0000000..efb476c --- /dev/null +++ b/usr/include/sys/sysmacros.h @@ -0,0 +1,37 @@ +/* + * sys/sysmacros.h + * + * Constructs to create and pick apart dev_t. The double-underscore + * versions are macros so they can be used as constants. + */ + +#ifndef _SYS_SYSMACROS_H +#define _SYS_SYSMACROS_H + +#include <klibc/compiler.h> +#include <sys/types.h> + +#define __major(__d) ((int)(((__d) >> 8) & 0xfffU)) +__static_inline int _major(dev_t __d) +{ + return __major(__d); +} +#define major(__d) _major(__d) + +#define __minor(__d) ((int)(((__d) & 0xffU)|(((__d) >> 12) & 0xfff00U))) +__static_inline int _minor(dev_t __d) +{ + return __minor(__d); +} +#define minor(__d) _minor(__d) + +#define __makedev(__ma, __mi) \ + ((dev_t)((((__ma) & 0xfffU) << 8)| \ + ((__mi) & 0xffU)|(((__mi) & 0xfff00U) << 12))) +__static_inline dev_t _makedev(int __ma, int __mi) +{ + return __makedev(__ma, __mi); +} +#define makedev(__ma, __mi) _makedev(__ma, __mi) + +#endif /* _SYS_SYSMACROS_H */ diff --git a/usr/include/sys/time.h b/usr/include/sys/time.h new file mode 100644 index 0000000..423d3d5 --- /dev/null +++ b/usr/include/sys/time.h @@ -0,0 +1,117 @@ +/* + * sys/time.h + */ + +#ifndef _SYS_TIME_H +#define _SYS_TIME_H + +#include <klibc/extern.h> +#include <klibc/endian.h> +#include <stddef.h> +#include <sys/types.h> + +/* struct timespec as used by current kernel UAPI (time64 on 32-bit) */ +struct timespec { + __kernel_time64_t tv_sec; +#if __BYTE_ORDER == __BIG_ENDIAN && __BITS_PER_LONG == 32 + int :32; +#endif + long tv_nsec; +#if __BYTE_ORDER == __LITTLE_ENDIAN && __BITS_PER_LONG == 32 + int :32; +#endif +}; + +/* struct timeval with 64-bit time, not used by kernel UAPI */ +struct timeval { + __kernel_time64_t tv_sec; + __kernel_suseconds_t tv_usec; +}; + +/* struct timeval as used by old kernel UAPI */ +struct timeval_old { + __kernel_time_t tv_sec; + __kernel_suseconds_t tv_usec; +}; + +struct itimerspec { + struct timespec it_interval; + struct timespec it_value; +}; + +struct itimerval { + struct timeval_old it_interval; + struct timeval_old it_value; +}; + +struct timezone { + int tz_minuteswest; + int tz_dsttime; +}; + +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +#define CLOCK_PROCESS_CPUTIME_ID 2 +#define CLOCK_THREAD_CPUTIME_ID 3 +#define CLOCK_MONOTONIC_RAW 4 +#define CLOCK_REALTIME_COARSE 5 +#define CLOCK_MONOTONIC_COARSE 6 +#define CLOCK_BOOTTIME 7 +#define CLOCK_REALTIME_ALARM 8 +#define CLOCK_BOOTTIME_ALARM 9 +#define CLOCK_TAI 11 + +#define TIMER_ABSTIME 0x01 + +/* The 2.6.20 Linux headers always #define FD_ZERO __FD_ZERO, etc, in + <linux/time.h> but not all architectures define the + double-underscore ones, except __NFDBITS, __FD_SETSIZE and + __FDSET_LONGS which are defined in <linux/posix_types.h>. + + Unfortunately, some architectures define the double-underscore ones + as inlines, so we can't use a simple #ifdef test. Thus, the only + safe option remaining is to #undef the top-level macros. */ + +#undef FD_ZERO +#undef FD_SET +#undef FD_CLR +#undef FD_ISSET +#undef FD_SETSIZE + +__extern void *memset(void *, int, size_t); +static inline void FD_ZERO(fd_set *__fdsetp) +{ + memset(__fdsetp, 0, sizeof(fd_set)); +} +static inline void FD_SET(int __fd, fd_set *__fdsetp) +{ + __fdsetp->fds_bits[__fd/BITS_PER_LONG] |= + (1UL << (__fd % BITS_PER_LONG)); +} +static inline void FD_CLR(int __fd, fd_set *__fdsetp) +{ + __fdsetp->fds_bits[__fd/BITS_PER_LONG] &= + ~(1UL << (__fd % BITS_PER_LONG)); +} +static inline int FD_ISSET(int __fd, fd_set *__fdsetp) +{ + return (__fdsetp->fds_bits[__fd/BITS_PER_LONG] >> + (__fd % BITS_PER_LONG)) & 1; +} + +#define FD_SETSIZE __FD_SETSIZE + +__extern int gettimeofday(struct timeval *, struct timezone *); +__extern int settimeofday(const struct timeval *, const struct timezone *); +__extern int clock_gettime(clockid_t, struct timespec *); +__extern int clock_settime(clockid_t, const struct timespec *); +__extern int clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *); +__extern int getitimer(int, struct itimerval *); +__extern int setitimer(int, const struct itimerval *, struct itimerval *); +__extern int utimes(const char *, const struct timeval[2]); + +#endif /* _SYS_TIME_H */ diff --git a/usr/include/sys/times.h b/usr/include/sys/times.h new file mode 100644 index 0000000..16be69a --- /dev/null +++ b/usr/include/sys/times.h @@ -0,0 +1,14 @@ +/* + * sys/times.h + */ + +#ifndef _SYS_TIMES_H +#define _SYS_TIMES_H + +#include <klibc/extern.h> +#include <sys/types.h> +#include <linux/times.h> + +__extern clock_t times(struct tms *); + +#endif /* _SYS_TIMES_H */ diff --git a/usr/include/sys/types.h b/usr/include/sys/types.h new file mode 100644 index 0000000..d698ae5 --- /dev/null +++ b/usr/include/sys/types.h @@ -0,0 +1,108 @@ +/* + * sys/types.h + */ + +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H + +#include <klibc/compiler.h> +#include <stddef.h> +#include <stdint.h> + +#define _SSIZE_T +/* __SIZE_TYPE__ defined either by GCC or <stddef.h> */ +#define unsigned /* nothing, temporarily */ +typedef signed __SIZE_TYPE__ ssize_t; +#undef unsigned + +#include <linux/posix_types.h> +#include <asm/types.h> + +/* Keeps linux/types.h from getting included elsewhere */ +#define _LINUX_TYPES_H + +typedef __kernel_fd_set fd_set; +typedef uint32_t dev_t; +typedef __kernel_ino_t ino_t; +typedef __kernel_mode_t mode_t; +typedef __kernel_loff_t off_t; +typedef __kernel_loff_t loff_t; +typedef __kernel_pid_t pid_t; +typedef __kernel_daddr_t daddr_t; +typedef __kernel_key_t key_t; +typedef __kernel_suseconds_t suseconds_t; +/* typedef __kernel_timer_t timer_t; */ +typedef int timer_t; +typedef __kernel_clockid_t clockid_t; + +typedef __kernel_uid32_t uid_t; +typedef __kernel_gid32_t gid_t; + +typedef __kernel_fsid_t fsid_t; + +/* + * The following typedefs are also protected by individual ifdefs for + * historical reasons: + */ +#ifndef _TIME_T +#define _TIME_T +typedef __kernel_time64_t time_t; +#endif + +#ifndef _CLOCK_T +#define _CLOCK_T +typedef __kernel_clock_t clock_t; +#endif + +#ifndef _CADDR_T +#define _CADDR_T +typedef __kernel_caddr_t caddr_t; +#endif + +/* BSD */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +/* SysV */ +typedef unsigned char unchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +/* More BSD */ +typedef uint8_t u_int8_t; +typedef uint16_t u_int16_t; +typedef uint32_t u_int32_t; +typedef uint64_t u_int64_t; + +typedef __u16 __bitwise __le16; +typedef __u16 __bitwise __be16; +typedef __u32 __bitwise __le32; +typedef __u32 __bitwise __be32; +typedef __u64 __bitwise __le64; +typedef __u64 __bitwise __be64; + +typedef __u16 __bitwise __sum16; +typedef __u32 __bitwise __sum32; +typedef __u64 __bitwise __sum64; +typedef __u32 __bitwise __wsum; + +#define __aligned_u64 __u64 __attribute__((aligned(8))) +#define __aligned_be64 __be64 __attribute__((aligned(8))) +#define __aligned_le64 __le64 __attribute__((aligned(8))) + +/* + * Some headers seem to require this... + */ +#ifndef BITS_PER_LONG +# define BITS_PER_LONG _BITSIZE +#endif + +/* + * Some apps want this in <sys/types.h> + */ +#include <sys/sysmacros.h> + +#endif diff --git a/usr/include/sys/uio.h b/usr/include/sys/uio.h new file mode 100644 index 0000000..ee341fb --- /dev/null +++ b/usr/include/sys/uio.h @@ -0,0 +1,15 @@ +/* + * sys/uio.h + */ + +#ifndef _SYS_UIO_H +#define _SYS_UIO_H + +#include <klibc/extern.h> +#include <sys/types.h> +#include <linux/uio.h> + +__extern int readv(int, const struct iovec *, int); +__extern int writev(int, const struct iovec *, int); + +#endif /* _SYS_UIO_H */ diff --git a/usr/include/sys/un.h b/usr/include/sys/un.h new file mode 100644 index 0000000..e599f67 --- /dev/null +++ b/usr/include/sys/un.h @@ -0,0 +1,11 @@ +/* + * <sys/un.h> + */ + +#ifndef _SYS_UN_H +#define _SYS_UN_H + +#include <sys/socket.h> +#include <linux/un.h> + +#endif /* _SYS_UN_H */ diff --git a/usr/include/sys/utime.h b/usr/include/sys/utime.h new file mode 100644 index 0000000..55415db --- /dev/null +++ b/usr/include/sys/utime.h @@ -0,0 +1,11 @@ +/* + * sys/utime.h + */ + +#ifndef _SYS_UTIME_H +#define _SYS_UTIME_H + +#include <sys/types.h> +#include <linux/utime.h> + +#endif /* _SYS_UTIME_H */ diff --git a/usr/include/sys/utsname.h b/usr/include/sys/utsname.h new file mode 100644 index 0000000..fd55c0b --- /dev/null +++ b/usr/include/sys/utsname.h @@ -0,0 +1,23 @@ +/* + * sys/utsname.h + */ + +#ifndef _SYS_UTSNAME_H +#define _SYS_UTSNAME_H + +#include <klibc/extern.h> + +#define SYS_NMLN 65 + +struct utsname { + char sysname[SYS_NMLN]; + char nodename[SYS_NMLN]; + char release[SYS_NMLN]; + char version[SYS_NMLN]; + char machine[SYS_NMLN]; + char domainname[SYS_NMLN]; +}; + +__extern int uname(struct utsname *); + +#endif /* _SYS_UTSNAME_H */ diff --git a/usr/include/sys/vfs.h b/usr/include/sys/vfs.h new file mode 100644 index 0000000..6fb1eab --- /dev/null +++ b/usr/include/sys/vfs.h @@ -0,0 +1,132 @@ +/* + * sys/vfs.h + */ + +#ifndef _SYS_VFS_H +#define _SYS_VFS_H + +#include <stdint.h> +#include <klibc/extern.h> +#include <sys/types.h> +#include <bitsize.h> +#include <klibc/sysconfig.h> + +/* struct statfs64 -- there seems to be two standards - + one for 32 and one for 64 bits, and they're incompatible. + Worse, some 64-bit platforms seem to use the 32-bit layout. + Of course, there is no includable header that does this well. */ + +#if _KLIBC_STATFS_F_TYPE_64 + +struct statfs { + uint64_t f_type; + uint64_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + __kernel_fsid_t f_fsid; + uint64_t f_namelen; + uint64_t f_frsize; + uint64_t f_spare[5]; +}; + +#elif _KLIBC_STATFS_F_TYPE_32B + +struct statfs { + uint32_t f_type; + uint32_t f_bsize; + uint32_t f_frsize; + uint32_t __pad; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_files; + uint64_t f_ffree; + uint64_t f_bavail; + __kernel_fsid_t f_fsid; + uint32_t f_namelen; + uint32_t f_spare[6]; +}; + +#else /* not _KLIBC_STATFS_F_TYPE_64 */ + +struct statfs { + uint32_t f_type; + uint32_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + __kernel_fsid_t f_fsid; + uint32_t f_namelen; + uint32_t f_frsize; + uint32_t f_spare[5]; +}; + +#endif /* _KLIBC_STATFS_F_TYPE_64 */ + +__extern int statfs(const char *, struct statfs *); +__extern int fstatfs(int, struct statfs *); + +/* Various filesystem types */ +#define ADFS_SUPER_MAGIC 0xadf5 +#define AFFS_SUPER_MAGIC 0xadff +#define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ +#define AUTOFS_SUPER_MAGIC 0x0187 +#define BFS_MAGIC 0x1BADFACE +#define CAPIFS_SUPER_MAGIC 0x434e +#define CIFS_MAGIC_NUMBER 0xFF534D42 +#define CODA_SUPER_MAGIC 0x73757245 +#define CRAMFS_MAGIC 0x28cd3d45 +#define DEVFS_SUPER_MAGIC 0x1373 +#define DEVPTS_SUPER_MAGIC 0x1cd1 +#define EFS_SUPER_MAGIC 0x414A53 +#define EVENTPOLLFS_MAGIC 0x03111965 +#define EXT2_SUPER_MAGIC 0xEF53 +#define EXT3_SUPER_MAGIC 0xEF53 +#define GADGETFS_MAGIC 0xaee71ee7 +#define HFSPLUS_SUPER_MAGIC 0x482b +#define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */ +#define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */ +#define HPFS_SUPER_MAGIC 0xf995e849 +#define HUGETLBFS_MAGIC 0x958458f6 +#define HWGFS_MAGIC 0x12061983 +#define IBMASMFS_MAGIC 0x66726f67 +#define ISOFS_SUPER_MAGIC 0x9660 +#define JFFS2_SUPER_MAGIC 0x72b6 +#define JFFS_MAGIC_BITMASK 0x34383931 /* "1984" */ +#define JFFS_MAGIC_SB_BITMASK 0x07c0 /* 1984 */ +#define JFS_SUPER_MAGIC 0x3153464a /* "JFS1" */ +#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */ +#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ +#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */ +#define NCP_SUPER_MAGIC 0x564c +#define NFS_SUPER_MAGIC 0x6969 +#define NFS_SUPER_MAGIC 0x6969 +#define OPENPROM_SUPER_MAGIC 0x9fa1 +#define OPROFILEFS_MAGIC 0x6f70726f +#define PFMFS_MAGIC 0xa0b4d889 +#define PIPEFS_MAGIC 0x50495045 +#define PROC_SUPER_MAGIC 0x9fa0 +#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */ +#define RAMFS_MAGIC 0x858458f6 +#define REISERFS_SUPER_MAGIC 0x52654973 +#define ROMFS_MAGIC 0x7275 +#define SMB_SUPER_MAGIC 0x517B +#define SOCKFS_MAGIC 0x534F434B +#define SYSFS_MAGIC 0x62656572 +#define TMPFS_MAGIC 0x01021994 +#define UDF_SUPER_MAGIC 0x15013346 +#define UFS_MAGIC 0x00011954 +#define UFS_MAGIC_4GB 0x05231994 /* fs > 4 GB && fs_featurebits */ +#define UFS_MAGIC_FEA 0x00195612 /* fs_featurebits supported */ +#define UFS_MAGIC_LFN 0x00095014 /* fs supports filenames > 14 chars */ +#define UFS_MAGIC_SEC 0x00612195 /* B1 security fs */ +#define USBDEVICE_SUPER_MAGIC 0x9fa2 +#define VXFS_SUPER_MAGIC 0xa501FCF5 + +#endif /* _SYS_VFS_H */ diff --git a/usr/include/sys/wait.h b/usr/include/sys/wait.h new file mode 100644 index 0000000..0f5efaf --- /dev/null +++ b/usr/include/sys/wait.h @@ -0,0 +1,28 @@ +/* + * sys/wait.h + */ + +#ifndef _SYS_WAIT_H +#define _SYS_WAIT_H + +#include <klibc/extern.h> +#include <sys/types.h> +#include <sys/resource.h> + +#include <linux/wait.h> + +#define WEXITSTATUS(s) (((s) & 0xff00) >> 8) +#define WTERMSIG(s) ((s) & 0x7f) +#define WIFEXITED(s) (WTERMSIG(s) == 0) +#define WIFSTOPPED(s) (WTERMSIG(s) == 0x7f) +/* Ugly hack to avoid multiple evaluation of "s" */ +#define WIFSIGNALED(s) (WTERMSIG((s)+1) >= 2) +#define WCOREDUMP(s) ((s) & 0x80) +#define WSTOPSIG(s) WEXITSTATUS(s) + +__extern pid_t wait(int *); +__extern pid_t waitpid(pid_t, int *, int); +__extern pid_t wait3(int *, int, struct rusage *); +__extern pid_t wait4(pid_t, int *, int, struct rusage *); + +#endif /* _SYS_WAIT_H */ diff --git a/usr/include/sysexits.h b/usr/include/sysexits.h new file mode 100644 index 0000000..ef454b9 --- /dev/null +++ b/usr/include/sysexits.h @@ -0,0 +1,26 @@ +#ifndef _SYSEXITS_H +#define _SYSEXITS_H + +#define EX_OK 0 /* successful termination */ + +#define EX__BASE 64 /* base value for error messages */ + +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ + +#define EX__MAX 78 /* maximum listed value */ + +#endif /* _SYSEXITS_H */ diff --git a/usr/include/syslog.h b/usr/include/syslog.h new file mode 100644 index 0000000..71a17e0 --- /dev/null +++ b/usr/include/syslog.h @@ -0,0 +1,62 @@ +/* + * syslog.h + */ + +#ifndef _SYSLOG_H +#define _SYSLOG_H + +#include <stdio.h> +#include <klibc/extern.h> + +/* Alert levels */ +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +#define LOG_PRIMASK 7 +#define LOG_PRI(x) ((x) & LOG_PRIMASK) + +/* Facilities; not actually used */ +#define LOG_KERN 0000 +#define LOG_USER 0010 +#define LOG_MAIL 0020 +#define LOG_DAEMON 0030 +#define LOG_AUTH 0040 +#define LOG_SYSLOG 0050 +#define LOG_LPR 0060 +#define LOG_NEWS 0070 +#define LOG_UUCP 0100 +#define LOG_CRON 0110 +#define LOG_AUTHPRIV 0120 +#define LOG_FTP 0130 +#define LOG_LOCAL0 0200 +#define LOG_LOCAL1 0210 +#define LOG_LOCAL2 0220 +#define LOG_LOCAL3 0230 +#define LOG_LOCAL4 0240 +#define LOG_LOCAL5 0250 +#define LOG_LOCAL6 0260 +#define LOG_LOCAL7 0270 + +#define LOG_FACMASK 01770 +#define LOG_FAC(x) (((x) >> 3) & (LOG_FACMASK >> 3)) + +/* openlog() flags; only LOG_PID and LOG_PERROR supported */ +#define LOG_PID 0x01 /* include pid with message */ +#define LOG_CONS 0x02 /* write to console on logger error */ +#define LOG_ODELAY 0x04 /* delay connection until syslog() */ +#define LOG_NDELAY 0x08 /* open connection immediately */ +#define LOG_NOWAIT 0x10 /* wait for child processes (unused on linux) */ +#define LOG_PERROR 0x20 /* additional logging to stderr */ + +__extern void openlog(const char *, int, int); +__extern void syslog(int, const char *, ...); +__extern void vsyslog(int, const char *, va_list); +__extern void closelog(void); + +#endif /* _SYSLOG_H */ diff --git a/usr/include/termios.h b/usr/include/termios.h new file mode 100644 index 0000000..59d9859 --- /dev/null +++ b/usr/include/termios.h @@ -0,0 +1,91 @@ +/* + * termios.h + */ + +#ifndef _TERMIOS_H +#define _TERMIOS_H + +#include <klibc/extern.h> +#include <stdint.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <linux/termios.h> + +/* Redefine these so the magic constants == the ioctl number to use. */ +#undef TCSANOW +#undef TCSADRAIN +#undef TCSAFLUSH +#define TCSANOW TCSETS +#define TCSADRAIN TCSETSW +#define TCSAFLUSH TCSETSF + +static __inline__ int tcgetattr(int __fd, struct termios *__s) +{ + return ioctl(__fd, TCGETS, __s); +} + +static __inline__ int tcsetattr(int __fd, int __opt, const struct termios *__s) +{ + return ioctl(__fd, __opt, (void *)__s); +} + +static __inline__ int tcflow(int __fd, int __action) +{ + return ioctl(__fd, TCXONC, (void *)(intptr_t) __action); +} + +static __inline__ int tcflush(int __fd, int __queue) +{ + return ioctl(__fd, TCFLSH, (void *)(intptr_t) __queue); +} + +static __inline__ int tcdrain(int __fd) +{ + return ioctl(__fd, TCSBRK, (void *)1L); +} + +static __inline__ pid_t tcgetpgrp(int __fd) +{ + pid_t __p; + return ioctl(__fd, TIOCGPGRP, &__p) ? (pid_t) - 1 : __p; +} + +static __inline__ pid_t tcgetsid(int __fd) +{ + pid_t __p; + return ioctl(__fd, TIOCGSID, &__p) ? (pid_t) - 1 : __p; +} + +static __inline__ int tcsendbreak(int __fd, int __duration) +{ + return ioctl(__fd, TCSBRKP, (void *)(uintptr_t) __duration); +} + +static __inline__ int tcsetpgrp(int __fd, pid_t __p) +{ + return ioctl(__fd, TIOCSPGRP, &__p); +} + +static __inline__ speed_t cfgetospeed(const struct termios *__s) +{ + return (speed_t) (__s->c_cflag & CBAUD); +} + +static __inline__ speed_t cfgetispeed(const struct termios *__s) +{ + return (speed_t) (__s->c_cflag & CBAUD); +} + +static __inline__ int cfsetospeed(struct termios *__s, speed_t __v) +{ + __s->c_cflag = (__s->c_cflag & ~CBAUD) | (__v & CBAUD); + return 0; +} + +static __inline__ int cfsetispeed(struct termios *__s, speed_t __v) +{ + __s->c_cflag = (__s->c_cflag & ~CBAUD) | (__v & CBAUD); + return 0; +} + +#endif /* _TERMIOS_H */ diff --git a/usr/include/time.h b/usr/include/time.h new file mode 100644 index 0000000..72b2993 --- /dev/null +++ b/usr/include/time.h @@ -0,0 +1,18 @@ +/* + * time.h + */ + +#ifndef _TIME_H +#define _TIME_H + +#include <klibc/extern.h> +#include <sys/time.h> + +__extern time_t time(time_t *); +__extern int nanosleep(const struct timespec *, struct timespec *); + +/* klibc-specific but useful since we don't have floating point */ +__extern char *strtotimeval(const char *str, struct timeval *tv); +__extern char *strtotimespec(const char *str, struct timespec *tv); + +#endif /* _TIME_H */ diff --git a/usr/include/unistd.h b/usr/include/unistd.h new file mode 100644 index 0000000..6f71f7a --- /dev/null +++ b/usr/include/unistd.h @@ -0,0 +1,167 @@ +/* + * unistd.h + */ + +#ifndef _UNISTD_H +#define _UNISTD_H + +#include <klibc/extern.h> +#include <klibc/compiler.h> +#include <klibc/seek.h> +#include <stddef.h> +#include <sys/types.h> +#include <sys/select.h> + +__extern char **environ; +__extern __noreturn _exit(int); + +__extern pid_t fork(void); +__extern pid_t vfork(void); +__extern pid_t getpid(void); +__extern pid_t getpgid(pid_t); +__extern int setpgid(pid_t, pid_t); +__extern pid_t getppid(void); +__extern pid_t getpgrp(void); +__extern int setpgrp(void); +__extern pid_t setsid(void); +__extern pid_t getsid(pid_t); +__extern int execv(const char *, char *const *); +__extern int execvp(const char *, char *const *); +__extern int execve(const char *, char *const *, char *const *); +__extern int execvpe(const char *, char *const *, char *const *); +__extern int execl(const char *, const char *, ...); +__extern int execlp(const char *, const char *, ...); +__extern int execle(const char *, const char *, ...); +__extern int execlpe(const char *, const char *, ...); + +__extern int nice(int); +__extern int setuid(uid_t); +__extern uid_t getuid(void); +__extern int seteuid(uid_t); +__extern uid_t geteuid(void); +__extern int setgid(gid_t); +__extern gid_t getgid(void); +__extern int setegid(gid_t); +__extern gid_t getegid(void); +__extern int getgroups(int, gid_t *); +__extern int setgroups(size_t, const gid_t *); +__extern int setreuid(uid_t, uid_t); +__extern int setregid(gid_t, gid_t); +__extern int setresuid(uid_t, uid_t, uid_t); +__extern int setresgid(gid_t, gid_t, gid_t); +__extern int getfsuid(uid_t); +__extern int setfsuid(uid_t); + +/* Macros for access() */ +#define R_OK 4 /* Read */ +#define W_OK 2 /* Write */ +#define X_OK 1 /* Execute */ +#define F_OK 0 /* Existence */ + +__extern int access(const char *, int); +__extern int faccessat(int, const char *, int, int); +__extern int link(const char *, const char *); +__extern int linkat(int, const char *, int, const char *, int); +__extern int unlink(const char *); +__extern int unlinkat(int, const char *, int); +__extern int chdir(const char *); +__extern int fchdir(int); +__extern int chmod(const char *, mode_t); +__extern int fchmod(int, mode_t); +__extern int mkdir(const char *, mode_t); +__extern int mkdirat(int, const char *, mode_t); +__extern int rmdir(const char *); +__extern int pipe(int[2]); +__extern int pipe2(int[2], int); +__extern int chroot(const char *); +__extern int symlink(const char *, const char *); +__extern int symlinkat(const char *, int, const char *); +__extern int readlink(const char *, char *, size_t); +__extern int readlinkat(int, const char *, char *, size_t); +__extern int chown(const char *, uid_t, gid_t); +__extern int fchown(int, uid_t, gid_t); +__extern int lchown(const char *, uid_t, gid_t); +__extern char *getcwd(char *, size_t); +__extern int fchownat(int, const char *, uid_t, gid_t, int); + +/* Also in <fcntl.h> */ +#ifndef _KLIBC_IN_OPEN_C +__extern int open(const char *, int, ...); +__extern int openat(int, const char *, int, ...); +#endif +__extern int creat(const char *, mode_t); +__extern int close(int); +__extern off_t lseek(int, off_t, int); +/* off_t is 64 bits now even on 32-bit platforms; see llseek.c */ +static __inline__ off_t llseek(int __f, off_t __o, int __w) +{ + return lseek(__f, __o, __w); +} + +__extern ssize_t read(int, void *, size_t); +__extern ssize_t write(int, const void *, size_t); +__extern ssize_t pread(int, void *, size_t, off_t); +__extern ssize_t pwrite(int, const void *, size_t, off_t); + +__extern int dup(int); +__extern int dup2(int, int); +__extern int dup3(int, int, int); +__extern int fcntl(int, int, ...); +__extern int ioctl(int, int, void *); +__extern int ftruncate(int, off_t); + +/* + * Macros for sync_file_range() + */ +#define SYNC_FILE_RANGE_WAIT_BEFORE 1 +#define SYNC_FILE_RANGE_WRITE 2 +#define SYNC_FILE_RANGE_WAIT_AFTER 4 + +__extern int sync(void); +__extern int fsync(int); +__extern int fdatasync(int); +__extern int sync_file_range(int, off_t, off_t, unsigned int); + +__extern int pause(void); +__extern unsigned int alarm(unsigned int); +__extern unsigned int sleep(unsigned int); +__extern void usleep(unsigned long); + +__extern int gethostname(char *, size_t); +__extern int sethostname(const char *, size_t); +__extern int getdomainname(char *, size_t); +__extern int setdomainname(const char *, size_t); + +__extern void *__brk(void *); +__extern int brk(void *); +__extern void *sbrk(ptrdiff_t); + +__extern int getopt(int, char *const *, const char *); +__extern char *optarg; +__extern int optind, opterr, optopt; + +__extern int isatty(int); + +__extern unsigned int __page_size; +__must_inline int getpagesize(void) +{ + return __page_size; +} + +__extern unsigned int __page_shift; +__must_inline int __getpageshift(void) +{ + return __page_shift; +} + +__extern int daemon(int, int); + +/* Standard file descriptor numbers. */ +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +/* This #include must be at the end */ +#include <sys/sysconf.h> + +#endif /* _UNISTD_H */ diff --git a/usr/include/utime.h b/usr/include/utime.h new file mode 100644 index 0000000..fa00604 --- /dev/null +++ b/usr/include/utime.h @@ -0,0 +1,14 @@ +/* + * utime.h + */ + +#ifndef _UTIME_H +#define _UTIME_H + +#include <klibc/extern.h> +#include <sys/types.h> +#include <linux/utime.h> + +__extern int utime(const char *, const struct utimbuf *); + +#endif /* _UTIME_H */ diff --git a/usr/include/zconf.h b/usr/include/zconf.h new file mode 100644 index 0000000..afe324f --- /dev/null +++ b/usr/include/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include <windows.h> + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include <sys/types.h> /* for off_t */ +# include <unistd.h> /* for SEEK_* and off_t */ +# ifdef VMS +# include <unixio.h> /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/usr/include/zlib.h b/usr/include/zlib.h new file mode 100644 index 0000000..0228179 --- /dev/null +++ b/usr/include/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/usr/kinit/.gitignore b/usr/kinit/.gitignore new file mode 100644 index 0000000..f5a4f24 --- /dev/null +++ b/usr/kinit/.gitignore @@ -0,0 +1,3 @@ +lib.a +kinit +kinit.shared diff --git a/usr/kinit/Kbuild b/usr/kinit/Kbuild new file mode 100644 index 0000000..6cd5ba6 --- /dev/null +++ b/usr/kinit/Kbuild @@ -0,0 +1,43 @@ +# +# Kbuild file for kinit +# + +# library part of kinit. Is used by programs in sub-directories (resume et al) +lib-y := name_to_dev.o devname.o getarg.o capabilities.o +# use lib for kinit +static/kinit-y := lib.a + +static/kinit-y += kinit.o do_mounts.o ramdisk_load.o initrd.o +static/kinit-y += getintfile.o readfile.o xpio.o +static/kinit-y += do_mounts_md.o do_mounts_mtd.o nfsroot.o + +static/kinit-y += ipconfig/ +static/kinit-y += nfsmount/ +static/kinit-y += run-init/ +static/kinit-y += fstype/ +static/kinit-y += resume/ + +static-y := static/kinit +shared-y := shared/kinit +shared/kinit-y := $(static/kinit-y) + +# Additional include paths files +KLIBCCFLAGS += -I$(srctree)/$(src)/fstype \ + -I$(srctree)/$(src)/ipconfig \ + -I$(srctree)/$(src)/nfsmount \ + -I$(srctree)/$(src)/resume \ + -I$(srctree)/$(src)/run-init + +# Cleaning +targets += static/kinit static/kinit.g shared/kinit shared/kinit.g +subdir- := fstype ipconfig nfsmount resume run-init + +# Clean deletes the static and shared dir +clean-dirs := static shared + +# install binary +ifdef KLIBCSHAREDFLAGS +install-y := shared/kinit +else +install-y := static/kinit +endif diff --git a/usr/kinit/README b/usr/kinit/README new file mode 100644 index 0000000..fa7f645 --- /dev/null +++ b/usr/kinit/README @@ -0,0 +1,9 @@ +kinit - tiny init program +------------------------- + +This program is intended for use as /sbin/init in an initramfs +environment. It currently replaces the kernel's ipconfig and nfsroot +code. + +-- +Bryan O'Sullivan (2003/05/05) diff --git a/usr/kinit/capabilities.c b/usr/kinit/capabilities.c new file mode 100644 index 0000000..2c61025 --- /dev/null +++ b/usr/kinit/capabilities.c @@ -0,0 +1,231 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved + * Author: mikew@google.com (Mike Waychison) + */ + +/* + * We have to include the klibc types.h here to keep the kernel's + * types.h from being used. + */ +#include <sys/types.h> + +#include <sys/capability.h> +#include <sys/prctl.h> +#include <sys/utsname.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "kinit.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define MAKE_CAP(cap) [cap] = { .cap_name = #cap } + +struct capability { + const char *cap_name; +} capabilities[] = { + MAKE_CAP(CAP_CHOWN), + MAKE_CAP(CAP_DAC_OVERRIDE), + MAKE_CAP(CAP_DAC_READ_SEARCH), + MAKE_CAP(CAP_FOWNER), + MAKE_CAP(CAP_FSETID), + MAKE_CAP(CAP_KILL), + MAKE_CAP(CAP_SETGID), + MAKE_CAP(CAP_SETUID), + MAKE_CAP(CAP_SETPCAP), + MAKE_CAP(CAP_LINUX_IMMUTABLE), + MAKE_CAP(CAP_NET_BIND_SERVICE), + MAKE_CAP(CAP_NET_BROADCAST), + MAKE_CAP(CAP_NET_ADMIN), + MAKE_CAP(CAP_NET_RAW), + MAKE_CAP(CAP_IPC_LOCK), + MAKE_CAP(CAP_IPC_OWNER), + MAKE_CAP(CAP_SYS_MODULE), + MAKE_CAP(CAP_SYS_RAWIO), + MAKE_CAP(CAP_SYS_CHROOT), + MAKE_CAP(CAP_SYS_PTRACE), + MAKE_CAP(CAP_SYS_PACCT), + MAKE_CAP(CAP_SYS_ADMIN), + MAKE_CAP(CAP_SYS_BOOT), + MAKE_CAP(CAP_SYS_NICE), + MAKE_CAP(CAP_SYS_RESOURCE), + MAKE_CAP(CAP_SYS_TIME), + MAKE_CAP(CAP_SYS_TTY_CONFIG), + MAKE_CAP(CAP_MKNOD), + MAKE_CAP(CAP_LEASE), + MAKE_CAP(CAP_AUDIT_WRITE), + MAKE_CAP(CAP_AUDIT_CONTROL), + MAKE_CAP(CAP_SETFCAP), + MAKE_CAP(CAP_MAC_OVERRIDE), + MAKE_CAP(CAP_MAC_ADMIN), + MAKE_CAP(CAP_SYSLOG), +}; + +static void fail(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +static void fail(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + exit(1); +} + +/* + * Find the capability ordinal by name, and return its ordinal. + * Returns -1 on failure. + */ +static int find_capability(const char *s) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(capabilities); i++) { + if (capabilities[i].cap_name + && strcasecmp(s, capabilities[i].cap_name) == 0) { + return i; + } + } + return -1; +} + +static void do_capset(int cap_ordinal) +{ + struct __user_cap_header_struct hdr; + struct __user_cap_data_struct caps[2]; + + /* Get the current capability mask */ + hdr.version = _LINUX_CAPABILITY_VERSION_3; + hdr.pid = getpid(); + if (capget(&hdr, caps)) { + perror("capget()"); + exit(1); + } + + /* Drop the bits */ + if (cap_ordinal < 32) + caps[0].inheritable &= ~(1U << cap_ordinal); + else + caps[1].inheritable &= ~(1U << (cap_ordinal - 32)); + + /* And drop the capability. */ + hdr.version = _LINUX_CAPABILITY_VERSION_3; + hdr.pid = getpid(); + if (capset(&hdr, caps)) + fail("Couldn't drop the capability \"%s\"\n", + capabilities[cap_ordinal].cap_name); +} + +static void do_bset(int cap_ordinal) +{ + int ret; + + ret = prctl(PR_CAPBSET_READ, cap_ordinal); + if (ret == 1) { + ret = prctl(PR_CAPBSET_DROP, cap_ordinal); + if (ret != 0) + fail("Error dropping capability %s from bset\n", + capabilities[cap_ordinal].cap_name); + } else if (ret < 0) + fail("Kernel doesn't recognize capability %d\n", cap_ordinal); +} + +static void do_usermodehelper_file(const char *filename, int cap_ordinal) +{ + uint32_t lo32, hi32; + FILE *file; + static const size_t buf_size = 80; + char buf[buf_size]; + char tail; + size_t bytes_read; + int ret; + + /* Try and open the file */ + file = fopen(filename, "r+"); + if (!file && errno == ENOENT) + fail("Could not disable usermode helpers capabilities as " + "%s is not available\n", filename); + if (!file) + fail("Failed to access file %s errno %d\n", filename, errno); + + /* Read and process the current bits */ + bytes_read = fread(buf, 1, buf_size - 1, file); + if (bytes_read == 0) + fail("Trouble reading %s\n", filename); + buf[bytes_read] = '\0'; + ret = sscanf(buf, "%u %u %c", &lo32, &hi32, &tail); + if (ret != 2) + fail("Failed to understand %s \"%s\"\n", filename, buf); + + /* Clear the bits in the local copy */ + if (cap_ordinal < 32) + lo32 &= ~(1 << cap_ordinal); + else + hi32 &= ~(1 << (cap_ordinal - 32)); + + /* Commit the new bit masks to the kernel */ + ret = fflush(file); + if (ret != 0) + fail("Failed on file %s to fflush %d\n", filename, ret); + sprintf(buf, "%u %u", lo32, hi32); + ret = fwrite(buf, 1, strlen(buf) + 1, file); + if (ret != 0) + fail("Failed to commit usermode helper bitmasks: %d\n", ret); + + /* Cleanup */ + fclose(file); +} + +static void do_usermodehelper(int cap_ordinal) +{ + static const char * const files[] = { + "/proc/sys/kernel/usermodehelper/bset", + "/proc/sys/kernel/usermodehelper/inheritable", + }; + int i; + + for (i = 0; i < ARRAY_SIZE(files); i++) + do_usermodehelper_file(files[i], cap_ordinal); +} + +static void drop_capability(int cap_ordinal) +{ + do_usermodehelper(cap_ordinal); + do_bset(cap_ordinal); + do_capset(cap_ordinal); + + printf("Dropped capability: %s\n", capabilities[cap_ordinal].cap_name); +} + +int drop_capabilities(const char *caps) +{ + char *s, *saveptr = NULL; + char *token; + + if (!caps) + return 0; + + /* Create a duplicate string that can be modified. */ + s = strdup(caps); + if (!s) + fail("Failed to drop caps as requested. Exiting\n"); + + token = strtok_r(s, ",", &saveptr); + while (token) { + int cap_ordinal = find_capability(token); + + if (cap_ordinal < 0) + fail("Could not understand capability name \"%s\" " + "on command line, failing init\n", token); + + drop_capability(cap_ordinal); + + token = strtok_r(NULL, ",", &saveptr); + } + + free(s); + return 0; +} diff --git a/usr/kinit/capabilities.h b/usr/kinit/capabilities.h new file mode 100644 index 0000000..a32a66a --- /dev/null +++ b/usr/kinit/capabilities.h @@ -0,0 +1,10 @@ +/* + * capabilities.h + */ + +#ifndef KINIT_CAPABILITIES_H +#define KINIT_CAPABILITIES_H + +int drop_capabilities(const char *caps); + +#endif /* KINIT_CAPABILITIES_H */ diff --git a/usr/kinit/devname.c b/usr/kinit/devname.c new file mode 100644 index 0000000..c327e3b --- /dev/null +++ b/usr/kinit/devname.c @@ -0,0 +1,116 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include <string.h> +#include <sys/types.h> +#include <sys/sysmacros.h> + +#include "kinit.h" + +/* + * Print the name of a block device. + */ +#define BUF_SIZE 512 + +static int scansysdir(char *namebuf, char *sysdir, dev_t dev) +{ + char *dirtailptr = strchr(sysdir, '\0'); + DIR *dir; + int done = 0; + struct dirent *de; + char *systail; + FILE *sysdev; + unsigned long ma, mi; + char *ep; + ssize_t rd; + + dir = opendir(sysdir); + if (!dir) + return 0; + + *dirtailptr++ = '/'; + + while (!done && (de = readdir(dir))) { + /* Assume if we see a dot-name in sysfs it's special */ + if (de->d_name[0] == '.') + continue; + + if (de->d_type != DT_UNKNOWN && de->d_type != DT_DIR) + continue; + + if (strlen(de->d_name) >= + (BUF_SIZE - 64) - (dirtailptr - sysdir)) + continue; /* Badness... */ + + strcpy(dirtailptr, de->d_name); + systail = strchr(sysdir, '\0'); + + strcpy(systail, "/dev"); + sysdev = fopen(sysdir, "r"); + if (!sysdev) + continue; + + /* Abusing the namebuf as temporary storage here. */ + rd = fread(namebuf, 1, BUF_SIZE, sysdev); + namebuf[rd] = '\0'; /* Just in case... */ + + fclose(sysdev); + + ma = strtoul(namebuf, &ep, 10); + if (ma != major(dev) || *ep != ':') + continue; + + mi = strtoul(ep + 1, &ep, 10); + if (*ep != '\n') + continue; + + if (mi == minor(dev)) { + /* Found it! */ + strcpy(namebuf, de->d_name); + done = 1; + } else { + /* we have a major number match, scan for partitions */ + *systail = '\0'; + done = scansysdir(namebuf, sysdir, dev); + } + } + + closedir(dir); + return done; +} + +const char *bdevname(dev_t dev) +{ + static char buf[BUF_SIZE]; + char sysdir[BUF_SIZE]; + char *p; + + strcpy(sysdir, "/sys/block"); + + if (!scansysdir(buf, sysdir, dev)) + strcpy(buf, "dev"); /* prints e.g. dev(3,5) */ + + p = strchr(buf, '\0'); + snprintf(p, sizeof buf - (p - buf), "(%d,%d)", major(dev), minor(dev)); + + return buf; +} + +#ifdef TEST_DEVNAME /* Standalone test */ + +int main(int argc, char *argv[]) +{ + dev_t dev; + int i; + + for (i = 1; i < argc; i++) { + dev = strtoul(argv[i], NULL, 0); + + printf("0x%08x = %s\n", (unsigned int)dev, bdevname(dev)); + } + + return 0; +} + +#endif /* TEST */ diff --git a/usr/kinit/do_mounts.c b/usr/kinit/do_mounts.c new file mode 100644 index 0000000..b648299 --- /dev/null +++ b/usr/kinit/do_mounts.c @@ -0,0 +1,533 @@ +#include <errno.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> +#include <mntent.h> + +#include "do_mounts.h" +#include "kinit.h" +#include "fstype.h" +#include "zlib.h" + +#ifndef MS_RELATIME +# define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */ +#endif + +#ifndef MS_STRICTATIME +# define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#endif + +/* + * The following mount option parsing was stolen from + * + * usr/utils/mount_opts.c + * + * and adapted to add some later mount flags. + */ +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +struct mount_opts { + const char str[16]; + unsigned long rwmask; + unsigned long rwset; + unsigned long rwnoset; +}; + +struct extra_opts { + char *str; + char *end; + int used_size; + int alloc_size; +}; + +/* + * These options define the function of "mount(2)". + */ +#define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE) + + +/* These must be in alphabetic order! */ +static const struct mount_opts options[] = { + /* name mask set noset */ + {"async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS}, + {"atime", MS_NOATIME, 0, MS_NOATIME}, + {"bind", MS_TYPE, MS_BIND, 0,}, + {"dev", MS_NODEV, 0, MS_NODEV}, + {"diratime", MS_NODIRATIME, 0, MS_NODIRATIME}, + {"dirsync", MS_DIRSYNC, MS_DIRSYNC, 0}, + {"exec", MS_NOEXEC, 0, MS_NOEXEC}, + {"move", MS_TYPE, MS_MOVE, 0}, + {"nodev", MS_NODEV, MS_NODEV, 0}, + {"noexec", MS_NOEXEC, MS_NOEXEC, 0}, + {"nosuid", MS_NOSUID, MS_NOSUID, 0}, + {"recurse", MS_REC, MS_REC, 0}, + {"relatime", MS_RELATIME, MS_RELATIME, 0}, + {"remount", MS_TYPE, MS_REMOUNT, 0}, + {"ro", MS_RDONLY, MS_RDONLY, 0}, + {"rw", MS_RDONLY, 0, MS_RDONLY}, + {"strictatime", MS_STRICTATIME, MS_STRICTATIME, 0}, + {"suid", MS_NOSUID, 0, MS_NOSUID}, + {"sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0}, + {"verbose", MS_VERBOSE, MS_VERBOSE, 0}, +}; + +/* + * Append 's' to 'extra->str'. 's' is a mount option that can't be turned into + * a flag. Return 0 on success, -1 on error. + */ +static int add_extra_option(struct extra_opts *extra, char *s) +{ + int len = strlen(s); + int newlen = extra->used_size + len; + + if (extra->str) + len++; /* +1 for ',' */ + + if (newlen >= extra->alloc_size) { + char *new; + + new = realloc(extra->str, newlen + 1); /* +1 for NUL */ + if (!new) { + if (extra->str) + free(extra->str); + return -1; + } + + extra->str = new; + extra->end = extra->str + extra->used_size; + extra->alloc_size = newlen; + } + + if (extra->used_size) { + *extra->end = ','; + extra->end++; + } + strcpy(extra->end, s); + extra->used_size += len; + + return 0; +} + +/* + * Parse the options in 'arg'; put numeric mount flags into 'flags' and + * the rest into 'extra'. Return 0 on success, -1 on error. + */ +static int +parse_mount_options(char *arg, unsigned long *flags, struct extra_opts *extra) +{ + char *s; + + while ((s = strsep(&arg, ",")) != NULL) { + char *opt = s; + unsigned int i; + int res; + int no = (s[0] == 'n' && s[1] == 'o'); + int found = 0; + + if (no) + s += 2; + + for (i = 0; i < ARRAY_SIZE(options); i++) { + + res = strcmp(s, options[i].str); + if (res == 0) { + found = 1; + *flags &= ~options[i].rwmask; + if (no) + *flags |= options[i].rwnoset; + else + *flags |= options[i].rwset; + break; + + /* If we're beyond 's' alphabetically, we're done */ + } else if (res < 0) + break; + } + if (! found) + if (add_extra_option(extra, opt) != 0) + return -1; + } + + return 0; +} + +/* Create the device node "name" */ +int create_dev(const char *name, dev_t dev) +{ + unlink(name); + return mknod(name, S_IFBLK | 0600, dev); +} + + +/* + * If there is not a block device for the input 'name', try to create one; if + * we can't that's okay. + */ +static void create_dev_if_not_present(const char *name) +{ + struct stat st; + dev_t dev; + + if (stat(name, &st) == 0) /* file present; we're done */ + return; + dev = name_to_dev_t(name); + if (dev) + (void) create_dev(name, dev); +} + + +/* mount a filesystem, possibly trying a set of different types */ +const char *mount_block(const char *source, const char *target, + const char *type, unsigned long flags, + const void *data) +{ + char *fslist, *p, *ep; + const char *rp; + ssize_t fsbytes; + int fd; + + if (type) { + dprintf("kinit: trying to mount %s on %s " + "with type %s, flags 0x%lx, data '%s'\n", + source, target, type, flags, (char *)data); + int rv = mount(source, target, type, flags, data); + + if (rv != 0) + dprintf("kinit: mount %s on %s failed " + "with errno = %d\n", + source, target, errno); + /* Mount readonly if necessary */ + if (rv == -1 && errno == EACCES && !(flags & MS_RDONLY)) + rv = mount(source, target, type, flags | MS_RDONLY, + data); + return rv ? NULL : type; + } + + /* If no type given, try to identify the type first; this + also takes care of specific ordering requirements, like + ext3 before ext2... */ + fd = open(source, O_RDONLY); + if (fd >= 0) { + int err = identify_fs(fd, &type, NULL, 0); + close(fd); + + if (!err && type) { + dprintf("kinit: %s appears to be a %s filesystem\n", + source, type); + type = mount_block(source, target, type, flags, data); + if (type) + return type; + } + } + + dprintf("kinit: failed to identify filesystem %s, trying all\n", + source); + + fsbytes = readfile("/proc/filesystems", &fslist); + + errno = EINVAL; + if (fsbytes < 0) + return NULL; + + p = fslist; + ep = fslist + fsbytes; + + rp = NULL; + + while (p < ep) { + type = p; + p = strchr(p, '\n'); + if (!p) + break; + *p++ = '\0'; + /* We can't mount a block device as a "nodev" fs */ + if (*type != '\t') + continue; + + type++; + rp = mount_block(source, target, type, flags, data); + if (rp) + break; + if (errno != EINVAL) + break; + } + + free(fslist); + return rp; +} + +/* mount the root filesystem from a block device */ +static int +mount_block_root(int argc, char *argv[], dev_t root_dev, + const char *type, unsigned long flags) +{ + const char *data, *rp; + + data = get_arg(argc, argv, "rootflags="); + create_dev("/dev/root", root_dev); + + errno = 0; + + if (type) { + if ((rp = mount_block("/dev/root", "/root", type, flags, data))) + goto ok; + if (errno != EINVAL) + goto bad; + } + + if (!errno + && (rp = mount_block("/dev/root", "/root", NULL, flags, data))) + goto ok; + +bad: + if (errno != EINVAL) { + /* + * Allow the user to distinguish between failed open + * and bad superblock on root device. + */ + fprintf(stderr, "%s: Cannot open root device %s\n", + progname, bdevname(root_dev)); + return -errno; + } else { + fprintf(stderr, "%s: Unable to mount root fs on device %s\n", + progname, bdevname(root_dev)); + return -ESRCH; + } + +ok: + printf("%s: Mounted root (%s filesystem)%s.\n", + progname, rp, (flags & MS_RDONLY) ? " readonly" : ""); + return 0; +} + +static int +mount_roots(int argc, char *argv[], const char *root_dev_name) +{ + char *roots = strdup(root_dev_name); + char *root; + const char *sep = ","; + char *saveptr; + int ret = -ESRCH; + + root = strtok_r(roots, sep, &saveptr); + while (root) { + dev_t root_dev; + + dprintf("kinit: trying to mount %s\n", root); + root_dev = name_to_dev_t(root); + ret = mount_root(argc, argv, root_dev, root); + if (!ret) + break; + root = strtok_r(NULL, sep, &saveptr); + } + free(roots); + return ret; +} + +int +mount_root(int argc, char *argv[], dev_t root_dev, const char *root_dev_name) +{ + unsigned long flags = MS_RDONLY | MS_VERBOSE; + int ret; + const char *type = get_arg(argc, argv, "rootfstype="); + + if (get_flag(argc, argv, "rw") > get_flag(argc, argv, "ro")) { + dprintf("kinit: mounting root rw\n"); + flags &= ~MS_RDONLY; + } + + if (type) { + if (!strcmp(type, "nfs")) + root_dev = Root_NFS; + else if (!strcmp(type, "jffs2") && !major(root_dev)) + root_dev = Root_MTD; + } + + switch (root_dev) { + case Root_NFS: + ret = mount_nfs_root(argc, argv, flags); + break; + case Root_MTD: + ret = mount_mtd_root(argc, argv, root_dev_name, type, flags); + break; + default: + ret = mount_block_root(argc, argv, root_dev, type, flags); + break; + } + + if (!ret) + chdir("/root"); + + return ret; +} + +/* Allocate a buffer and prepend '/root' onto 'src'. */ +static char *prepend_root_dir(const char *src) +{ + size_t len = strlen(src) + 6; /* "/root" */ + char *p = malloc(len); + + if (!p) + return NULL; + + strcpy(p, "/root"); + strcat(p, src); + return p; +} + +int do_cmdline_mounts(int argc, char *argv[]) +{ + int arg_i; + int ret = 0; + + for (arg_i = 0; arg_i < argc; arg_i++) { + const char *fs_dev, *fs_dir, *fs_type; + char *fs_opts; + unsigned long flags = 0; + char *saveptr = NULL; + char *new_dir; + struct extra_opts extra = { 0, 0, 0, 0 }; + + if (strncmp(argv[arg_i], "kinit_mount=", 12)) + continue; + /* + * Format: + * <fs_dev>;<dir>;<fs_type>;[opt1],[optn...] + */ + fs_dev = strtok_r(&argv[arg_i][12], ";", &saveptr); + if (!fs_dev) { + fprintf(stderr, "Failed to parse fs_dev\n"); + continue; + } + fs_dir = strtok_r(NULL, ";", &saveptr); + if (!fs_dir) { + fprintf(stderr, "Failed to parse fs_dir\n"); + continue; + } + fs_type = strtok_r(NULL, ";", &saveptr); + if (!fs_type) { + fprintf(stderr, "Failed to parse fs_type\n"); + continue; + } + fs_opts = strtok_r(NULL, ";", &saveptr); + /* Don't error if there is no option string sent */ + + new_dir = prepend_root_dir(fs_dir); + if (! new_dir) + return -ENOMEM; + create_dev_if_not_present(fs_dev); + ret = parse_mount_options(fs_opts, &flags, &extra); + if (ret != 0) + break; + + if (!mount_block(fs_dev, new_dir, fs_type, + flags, extra.str)) + fprintf(stderr, "Skipping failed mount '%s'\n", fs_dev); + free(new_dir); + if (extra.str) + free(extra.str); + } + return ret; +} + +int do_fstab_mounts(FILE *fp) +{ + struct mntent *ent = NULL; + char *new_dir; + int ret = 0; + + while ((ent = getmntent(fp))) { + unsigned long flags = 0; + struct extra_opts extra = { 0, 0, 0, 0 }; + + new_dir = prepend_root_dir(ent->mnt_dir); + if (! new_dir) + return -ENOMEM; + create_dev_if_not_present(ent->mnt_fsname); + ret = parse_mount_options(ent->mnt_opts, &flags, &extra); + if (ret != 0) + break; + + if (!mount_block(ent->mnt_fsname, + new_dir, + ent->mnt_type, + flags, + extra.str)) { + fprintf(stderr, "Skipping failed mount '%s'\n", + ent->mnt_fsname); + } + free(new_dir); + if (extra.str) + free(extra.str); + } + return 0; +} + +int do_mounts(int argc, char *argv[]) +{ + const char *root_dev_name = get_arg(argc, argv, "root="); + const char *root_delay = get_arg(argc, argv, "rootdelay="); + const char *load_ramdisk = get_arg(argc, argv, "load_ramdisk="); + dev_t root_dev = 0; + int err; + FILE *fp; + + dprintf("kinit: do_mounts\n"); + + if (root_delay) { + int delay = atoi(root_delay); + fprintf(stderr, "Waiting %d s before mounting root device...\n", + delay); + sleep(delay); + } + + md_run(argc, argv); + + if (root_dev_name) { + root_dev = name_to_dev_t(root_dev_name); + } else if (get_arg(argc, argv, "nfsroot=") || + get_arg(argc, argv, "nfsaddrs=")) { + root_dev = Root_NFS; + } else { + long rootdev; + getintfile("/proc/sys/kernel/real-root-dev", &rootdev); + root_dev = (dev_t) rootdev; + } + + dprintf("kinit: root_dev = %s\n", bdevname(root_dev)); + + if (initrd_load(argc, argv, root_dev)) { + dprintf("initrd loaded\n"); + return 0; + } + + if (load_ramdisk && atoi(load_ramdisk)) { + if (ramdisk_load(argc, argv)) + root_dev = Root_RAM0; + } + + if (root_dev == Root_MULTI) + err = mount_roots(argc, argv, root_dev_name); + else + err = mount_root(argc, argv, root_dev, root_dev_name); + + if (err) + return err; + + if ((fp = setmntent("/etc/fstab", "r"))) { + err = do_fstab_mounts(fp); + fclose(fp); + } + + if (err) + return err; + + if (get_arg(argc, argv, "kinit_mount=")) + err = do_cmdline_mounts(argc, argv); + return err; +} diff --git a/usr/kinit/do_mounts.h b/usr/kinit/do_mounts.h new file mode 100644 index 0000000..99bc6a6 --- /dev/null +++ b/usr/kinit/do_mounts.h @@ -0,0 +1,49 @@ +/* + * do_mounts.h + */ + +#ifndef DO_MOUNTS_H +#define DO_MOUNTS_H + +#include <sys/types.h> +#include <sys/sysmacros.h> +#include <sys/stat.h> + +#define Root_RAM0 __makedev(1, 0) + +/* These device numbers are only used internally */ +#define Root_NFS __makedev(0, 255) +#define Root_MTD __makedev(0, 254) +#define Root_MULTI __makedev(0, 253) + +int create_dev(const char *name, dev_t dev); + +dev_t name_to_dev_t(const char *name); + +const char *mount_block(const char *source, const char *target, + const char *type, unsigned long flags, + const void *data); + +int mount_root(int argc, char *argv[], dev_t root_dev, + const char *root_dev_name); + +int mount_mtd_root(int argc, char *argv[], const char *root_dev_name, + const char *type, unsigned long flags); + +int do_mounts(int argc, char *argv[]); + +int initrd_load(int argc, char *argv[], dev_t root_dev); + +static inline dev_t bstat(const char *name) +{ + struct stat st; + + if (stat(name, &st) || !S_ISBLK(st.st_mode)) + return 0; + return st.st_rdev; +} + +int load_ramdisk_compressed(const char *devpath, FILE * wfd, + off_t ramdisk_start); + +#endif /* DO_MOUNTS_H */ diff --git a/usr/kinit/do_mounts_md.c b/usr/kinit/do_mounts_md.c new file mode 100644 index 0000000..f446620 --- /dev/null +++ b/usr/kinit/do_mounts_md.c @@ -0,0 +1,400 @@ +/* + * Handle autoconfiguration of md devices. This is ugly, partially since + * it still relies on a sizable kernel component. + * + * This file is derived from the Linux kernel. + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> +#include <sys/sysmacros.h> +#include <sys/md.h> +#include <linux/major.h> + +#include "kinit.h" +#include "do_mounts.h" + +#define LEVEL_NONE (-1000000) + +/* + * When md (and any require personalities) are compiled into the kernel + * (not a module), arrays can be assembles are boot time using with AUTODETECT + * where specially marked partitions are registered with md_autodetect_dev(), + * and with MD_BOOT where devices to be collected are given on the boot line + * with md=..... + * The code for that is here. + */ + +static int raid_noautodetect, raid_autopart; + +static struct { + int minor; + int partitioned; + int level; + int chunk; + char *device_names; +} md_setup_args[MAX_MD_DEVS]; + +static int md_setup_ents; + +/** + * get_option - Parse integer from an option string + * @str: option string + * @pint: (output) integer value parsed from @str + * + * Read an int from an option string; if available accept a subsequent + * comma as well. + * + * Return values: + * 0 : no int in string + * 1 : int found, no subsequent comma + * 2 : int found including a subsequent comma + */ + +static int get_option(char **str, int *pint) +{ + char *cur = *str; + + if (!cur || !(*cur)) + return 0; + *pint = strtol(cur, str, 0); + if (cur == *str) + return 0; + if (**str == ',') { + (*str)++; + return 2; + } + + return 1; +} + +/* + * Find the partitioned md device major number... of course this *HAD* + * to be done dynamically instead of using a registered number. + * Sigh. Double sigh. + */ +static int mdp_major(void) +{ + static int found = 0; + FILE *f; + char line[512], *p; + int is_blk, major_no; + + if (found) + return found; + + f = fopen("/proc/devices", "r"); + is_blk = 0; + while (fgets(line, sizeof line, f)) { + if (!strcmp(line, "Block devices:\n")) + is_blk = 1; + if (is_blk) { + major_no = strtol(line, &p, 10); + while (*p && isspace(*p)) + p++; + + if (major_no == 0) /* Not a number */ + is_blk = 0; + else if (major_no > 0 && !strcmp(p, "mdp")) { + found = major_no; + break; + } + } + } + fclose(f); + + if (!found) { + fprintf(stderr, + "Error: mdp devices detected but no mdp device found!\n"); + exit(1); + } + + return found; +} + +/* + * Parse the command-line parameters given our kernel, but do not + * actually try to invoke the MD device now; that is handled by + * md_setup_drive after the low-level disk drivers have initialised. + * + * 27/11/1999: Fixed to work correctly with the 2.3 kernel (which + * assigns the task of parsing integer arguments to the + * invoked program now). Added ability to initialise all + * the MD devices (by specifying multiple "md=" lines) + * instead of just one. -- KTK + * 18May2000: Added support for persistent-superblock arrays: + * md=n,0,factor,fault,device-list uses RAID0 for device n + * md=n,-1,factor,fault,device-list uses LINEAR for device n + * md=n,device-list reads a RAID superblock from the devices + * elements in device-list are read by name_to_kdev_t so can be + * a hex number or something like /dev/hda1 /dev/sdb + * 2001-06-03: Dave Cinege <dcinege@psychosis.com> + * Shifted name_to_kdev_t() and related operations to md_set_drive() + * for later execution. Rewrote section to make devfs compatible. + */ +static int md_setup(char *str) +{ + int minor_num, level, factor, fault, partitioned = 0; + char *pername = ""; + char *str1; + int ent; + + if (*str == 'd') { + partitioned = 1; + str++; + } + if (get_option(&str, &minor_num) != 2) { /* MD Number */ + fprintf(stderr, "md: Too few arguments supplied to md=.\n"); + return 0; + } + str1 = str; + if (minor_num >= MAX_MD_DEVS) { + fprintf(stderr, "md: md=%d, Minor device number too high.\n", + minor_num); + return 0; + } + for (ent = 0; ent < md_setup_ents; ent++) + if (md_setup_args[ent].minor == minor_num && + md_setup_args[ent].partitioned == partitioned) { + fprintf(stderr, + "md: md=%s%d, Specified more than once. " + "Replacing previous definition.\n", + partitioned ? "d" : "", minor_num); + break; + } + if (ent >= MAX_MD_DEVS) { + fprintf(stderr, "md: md=%s%d - too many md initialisations\n", + partitioned ? "d" : "", minor_num); + return 0; + } + if (ent >= md_setup_ents) + md_setup_ents++; + switch (get_option(&str, &level)) { /* RAID level */ + case 2: /* could be 0 or -1.. */ + if (level == 0 || level == LEVEL_LINEAR) { + if (get_option(&str, &factor) != 2 || /* Chunk Size */ + get_option(&str, &fault) != 2) { + fprintf(stderr, + "md: Too few arguments supplied to md=.\n"); + return 0; + } + md_setup_args[ent].level = level; + md_setup_args[ent].chunk = 1 << (factor + 12); + if (level == LEVEL_LINEAR) + pername = "linear"; + else + pername = "raid0"; + break; + } + /* FALL THROUGH */ + case 1: /* the first device is numeric */ + str = str1; + /* FALL THROUGH */ + case 0: + md_setup_args[ent].level = LEVEL_NONE; + pername = "super-block"; + } + + fprintf(stderr, "md: Will configure md%s%d (%s) from %s, below.\n", + partitioned ? "_d" : "", minor_num, pername, str); + md_setup_args[ent].device_names = str; + md_setup_args[ent].partitioned = partitioned; + md_setup_args[ent].minor = minor_num; + + return 1; +} + +#define MdpMinorShift 6 + +static void md_setup_drive(void) +{ + int dev_minor, i, ent, partitioned; + dev_t dev; + dev_t devices[MD_SB_DISKS + 1]; + + for (ent = 0; ent < md_setup_ents; ent++) { + int fd; + int err = 0; + char *devname; + mdu_disk_info_t dinfo; + char name[16]; + struct stat st_chk; + + dev_minor = md_setup_args[ent].minor; + partitioned = md_setup_args[ent].partitioned; + devname = md_setup_args[ent].device_names; + + snprintf(name, sizeof name, + "/dev/md%s%d", partitioned ? "_d" : "", dev_minor); + + if (stat(name, &st_chk) == 0) + continue; + + if (partitioned) + dev = makedev(mdp_major(), dev_minor << MdpMinorShift); + else + dev = makedev(MD_MAJOR, dev_minor); + create_dev(name, dev); + for (i = 0; i < MD_SB_DISKS && devname != 0; i++) { + char *p; + + p = strchr(devname, ','); + if (p) + *p++ = 0; + + dev = name_to_dev_t(devname); + if (!dev) { + fprintf(stderr, "md: Unknown device name: %s\n", + devname); + break; + } + + devices[i] = dev; + + devname = p; + } + devices[i] = 0; + + if (!i) + continue; + + fprintf(stderr, "md: Loading md%s%d: %s\n", + partitioned ? "_d" : "", dev_minor, + md_setup_args[ent].device_names); + + fd = open(name, 0, 0); + if (fd < 0) { + fprintf(stderr, "md: open failed - cannot start " + "array %s\n", name); + continue; + } + if (ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) { + fprintf(stderr, + "md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n", + dev_minor); + close(fd); + continue; + } + + if (md_setup_args[ent].level != LEVEL_NONE) { + /* non-persistent */ + mdu_array_info_t ainfo; + ainfo.level = md_setup_args[ent].level; + ainfo.size = 0; + ainfo.nr_disks = 0; + ainfo.raid_disks = 0; + while (devices[ainfo.raid_disks]) + ainfo.raid_disks++; + ainfo.md_minor = dev_minor; + ainfo.not_persistent = 1; + + ainfo.state = (1 << MD_SB_CLEAN); + ainfo.layout = 0; + ainfo.chunk_size = md_setup_args[ent].chunk; + err = ioctl(fd, SET_ARRAY_INFO, &ainfo); + for (i = 0; !err && i <= MD_SB_DISKS; i++) { + dev = devices[i]; + if (!dev) + break; + dinfo.number = i; + dinfo.raid_disk = i; + dinfo.state = + (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC); + dinfo.major = major(dev); + dinfo.minor = minor(dev); + err = ioctl(fd, ADD_NEW_DISK, &dinfo); + } + } else { + /* persistent */ + for (i = 0; i <= MD_SB_DISKS; i++) { + dev = devices[i]; + if (!dev) + break; + dinfo.major = major(dev); + dinfo.minor = minor(dev); + ioctl(fd, ADD_NEW_DISK, &dinfo); + } + } + if (!err) + err = ioctl(fd, RUN_ARRAY, 0); + if (err) + fprintf(stderr, "md: starting md%d failed\n", + dev_minor); + else { + /* reread the partition table. + * I (neilb) and not sure why this is needed, but I + * cannot boot a kernel with devfs compiled in from + * partitioned md array without it + */ + close(fd); + fd = open(name, 0, 0); + ioctl(fd, BLKRRPART, 0); + } + close(fd); + } +} + +static int raid_setup(char *str) +{ + int len, pos; + + len = strlen(str) + 1; + pos = 0; + + while (pos < len) { + char *comma = strchr(str + pos, ','); + int wlen; + if (comma) + wlen = (comma - str) - pos; + else + wlen = (len - 1) - pos; + + if (!strncmp(str, "noautodetect", wlen)) + raid_noautodetect = 1; + if (strncmp(str, "partitionable", wlen) == 0) + raid_autopart = 1; + if (strncmp(str, "part", wlen) == 0) + raid_autopart = 1; + pos += wlen + 1; + } + return 1; +} + +static void md_run_setup(void) +{ + create_dev("/dev/md0", makedev(MD_MAJOR, 0)); + if (raid_noautodetect) + fprintf(stderr, + "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n"); + else { + int fd = open("/dev/md0", 0, 0); + if (fd >= 0) { + ioctl(fd, RAID_AUTORUN, + (void *)(intptr_t) raid_autopart); + close(fd); + } + } + md_setup_drive(); +} + +void md_run(int argc, char *argv[]) +{ + char **pp, *p; + + for (pp = argv; (p = *pp); pp++) { + if (!strncmp(p, "raid=", 5)) + raid_setup(p + 5); + else if (!strncmp(p, "md=", 3)) + md_setup(p + 3); + } + + md_run_setup(); +} diff --git a/usr/kinit/do_mounts_mtd.c b/usr/kinit/do_mounts_mtd.c new file mode 100644 index 0000000..20d27ca --- /dev/null +++ b/usr/kinit/do_mounts_mtd.c @@ -0,0 +1,42 @@ +/* + * Mount an MTD device as a character device. + */ + +#include <errno.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> + +#include "kinit.h" +#include "do_mounts.h" + +int mount_mtd_root(int argc, char *argv[], const char *root_dev_name, + const char *type, unsigned long flags) +{ + const char *data = get_arg(argc, argv, "rootflags="); + + if (!type) + type = "jffs2"; + + printf("Trying to mount MTD %s as root (%s filesystem)\n", + root_dev_name, type); + + if (mount(root_dev_name, "/root", type, flags, data)) { + int err = errno; + fprintf(stderr, + "%s: Unable to mount MTD %s (%s filesystem) " + "as root: %s\n", + progname, root_dev_name, type, strerror(err)); + return -err; + } else { + fprintf(stderr, "%s: Mounted root (%s filesystem)%s.\n", + progname, type, (flags & MS_RDONLY) ? " readonly" : ""); + return 0; + } + +} diff --git a/usr/kinit/fstype/Kbuild b/usr/kinit/fstype/Kbuild new file mode 100644 index 0000000..631eb32 --- /dev/null +++ b/usr/kinit/fstype/Kbuild @@ -0,0 +1,29 @@ +# +# Kbuild file for fstype +# + +static-y := static/fstype +shared-y := shared/fstype + +# common .o files +objs := main.o fstype.o + +# TODO - do we want a stripped version +# TODO - do we want the static.g + shared.g directories? + +# Create built-in.o with all object files (used by kinit) +lib-y := $(objs) + +# .o files used to built executables +static/fstype-y := $(objs) +shared/fstype-y := $(objs) + +# Cleaning +clean-dirs := static shared + +# install binary +ifdef KLIBCSHAREDFLAGS +install-y := $(shared-y) +else +install-y := $(static-y) +endif diff --git a/usr/kinit/fstype/btrfs.h b/usr/kinit/fstype/btrfs.h new file mode 100644 index 0000000..459da12 --- /dev/null +++ b/usr/kinit/fstype/btrfs.h @@ -0,0 +1,57 @@ +#ifndef __BTRFS_H +#define __BTRFS_H + +# define BTRFS_MAGIC "_BHRfS_M" +# define BTRFS_MAGIC_L 8 + +/* + * Structure of the super block + */ +struct btrfs_super_block { + uint8_t csum[32]; + uint8_t fsid[16]; + uint64_t bytenr; + uint64_t flags; + uint8_t magic[8]; + uint64_t generation; + uint64_t root; + uint64_t chunk_root; + uint64_t log_root; + uint64_t log_root_transid; + uint64_t total_bytes; + uint64_t bytes_used; + uint64_t root_dir_objectid; + uint64_t num_devices; + uint32_t sectorsize; + uint32_t nodesize; + uint32_t leafsize; + uint32_t stripesize; + uint32_t sys_chunk_array_size; + uint64_t chunk_root_generation; + uint64_t compat_flags; + uint64_t compat_ro_flags; + uint64_t incompat_flags; + uint16_t csum_type; + uint8_t root_level; + uint8_t chunk_root_level; + uint8_t log_root_level; + struct btrfs_dev_item { + uint64_t devid; + uint64_t total_bytes; + uint64_t bytes_used; + uint32_t io_align; + uint32_t io_width; + uint32_t sector_size; + uint64_t type; + uint64_t generation; + uint64_t start_offset; + uint32_t dev_group; + uint8_t seek_speed; + uint8_t bandwidth; + uint8_t uuid[16]; + uint8_t fsid[16]; + } __attribute__ ((__packed__)) dev_item; + uint8_t label[256]; +} __attribute__ ((__packed__)); + +#endif /* __BTRFS_H */ diff --git a/usr/kinit/fstype/cramfs_fs.h b/usr/kinit/fstype/cramfs_fs.h new file mode 100644 index 0000000..6f5ad4f --- /dev/null +++ b/usr/kinit/fstype/cramfs_fs.h @@ -0,0 +1,85 @@ +#ifndef __CRAMFS_H +#define __CRAMFS_H + +#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */ +#define CRAMFS_SIGNATURE "Compressed ROMFS" + +/* + * Width of various bitfields in struct cramfs_inode. + * Primarily used to generate warnings in mkcramfs. + */ +#define CRAMFS_MODE_WIDTH 16 +#define CRAMFS_UID_WIDTH 16 +#define CRAMFS_SIZE_WIDTH 24 +#define CRAMFS_GID_WIDTH 8 +#define CRAMFS_NAMELEN_WIDTH 6 +#define CRAMFS_OFFSET_WIDTH 26 + +/* + * Since inode.namelen is a unsigned 6-bit number, the maximum cramfs + * path length is 63 << 2 = 252. + */ +#define CRAMFS_MAXPATHLEN (((1 << CRAMFS_NAMELEN_WIDTH) - 1) << 2) + +/* + * Reasonably terse representation of the inode data. + */ +struct cramfs_inode { + __u32 mode:CRAMFS_MODE_WIDTH, uid:CRAMFS_UID_WIDTH; + /* SIZE for device files is i_rdev */ + __u32 size:CRAMFS_SIZE_WIDTH, gid:CRAMFS_GID_WIDTH; + /* NAMELEN is the length of the file name, divided by 4 and + rounded up. (cramfs doesn't support hard links.) */ + /* OFFSET: For symlinks and non-empty regular files, this + contains the offset (divided by 4) of the file data in + compressed form (starting with an array of block pointers; + see README). For non-empty directories it is the offset + (divided by 4) of the inode of the first file in that + directory. For anything else, offset is zero. */ + __u32 namelen:CRAMFS_NAMELEN_WIDTH, offset:CRAMFS_OFFSET_WIDTH; +}; + +struct cramfs_info { + __u32 crc; + __u32 edition; + __u32 blocks; + __u32 files; +}; + +/* + * Superblock information at the beginning of the FS. + */ +struct cramfs_super { + __u32 magic; /* 0x28cd3d45 - random number */ + __u32 size; /* length in bytes */ + __u32 flags; /* feature flags */ + __u32 future; /* reserved for future use */ + __u8 signature[16]; /* "Compressed ROMFS" */ + struct cramfs_info fsid; /* unique filesystem info */ + __u8 name[16]; /* user-defined name */ + struct cramfs_inode root; /* root inode data */ +}; + +/* + * Feature flags + * + * 0x00000000 - 0x000000ff: features that work for all past kernels + * 0x00000100 - 0xffffffff: features that don't work for past kernels + */ +#define CRAMFS_FLAG_FSID_VERSION_2 0x00000001 /* fsid version #2 */ +#define CRAMFS_FLAG_SORTED_DIRS 0x00000002 /* sorted dirs */ +#define CRAMFS_FLAG_HOLES 0x00000100 /* support for holes */ +#define CRAMFS_FLAG_WRONG_SIGNATURE 0x00000200 /* reserved */ +#define CRAMFS_FLAG_SHIFTED_ROOT_OFFSET 0x00000400 /* shifted root fs */ + +/* + * Valid values in super.flags. Currently we refuse to mount + * if (flags & ~CRAMFS_SUPPORTED_FLAGS). Maybe that should be + * changed to test super.future instead. + */ +#define CRAMFS_SUPPORTED_FLAGS ( 0x000000ff \ + | CRAMFS_FLAG_HOLES \ + | CRAMFS_FLAG_WRONG_SIGNATURE \ + | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET ) + +#endif diff --git a/usr/kinit/fstype/ext2_fs.h b/usr/kinit/fstype/ext2_fs.h new file mode 100644 index 0000000..775df8f --- /dev/null +++ b/usr/kinit/fstype/ext2_fs.h @@ -0,0 +1,84 @@ +#ifndef __EXT2_FS_H +#define __EXT2_FS_H + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +/* + * Structure of the super block + */ +struct ext2_super_block { + __le32 s_inodes_count; /* Inodes count */ + __le32 s_blocks_count; /* Blocks count */ + __le32 s_r_blocks_count; /* Reserved blocks count */ + __le32 s_free_blocks_count; /* Free blocks count */ + __le32 s_free_inodes_count; /* Free inodes count */ + __le32 s_first_data_block; /* First Data Block */ + __le32 s_log_block_size; /* Block size */ + __le32 s_log_frag_size; /* Fragment size */ + __le32 s_blocks_per_group; /* # Blocks per group */ + __le32 s_frags_per_group; /* # Fragments per group */ + __le32 s_inodes_per_group; /* # Inodes per group */ + __le32 s_mtime; /* Mount time */ + __le32 s_wtime; /* Write time */ + __le16 s_mnt_count; /* Mount count */ + __le16 s_max_mnt_count; /* Maximal mount count */ + __le16 s_magic; /* Magic signature */ + __le16 s_state; /* File system state */ + __le16 s_errors; /* Behaviour when detecting errors */ + __le16 s_minor_rev_level; /* minor revision level */ + __le32 s_lastcheck; /* time of last check */ + __le32 s_checkinterval; /* max. time between checks */ + __le32 s_creator_os; /* OS */ + __le32 s_rev_level; /* Revision level */ + __le16 s_def_resuid; /* Default uid for reserved blocks */ + __le16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __le32 s_first_ino; /* First non-reserved inode */ + __le16 s_inode_size; /* size of inode structure */ + __le16 s_block_group_nr; /* block group # of this superblock */ + __le32 s_feature_compat; /* compatible feature set */ + __le32 s_feature_incompat; /* incompatible feature set */ + __le32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __le32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate */ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_padding1; + /* + * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_reserved_char_pad; + __u16 s_reserved_word_pad; + __le32 s_default_mount_opts; + __le32 s_first_meta_bg; /* First metablock block group */ + __u32 s_reserved[190]; /* Padding to the end of the block */ +}; + +#endif /* __EXT2_FS_H */ diff --git a/usr/kinit/fstype/ext3_fs.h b/usr/kinit/fstype/ext3_fs.h new file mode 100644 index 0000000..f958e5c --- /dev/null +++ b/usr/kinit/fstype/ext3_fs.h @@ -0,0 +1,134 @@ +#ifndef __EXT3_FS_H +#define __EXT3_FS_H + +/* + * The second extended file system magic number + */ +#define EXT3_SUPER_MAGIC 0xEF53 + +#define EXT2_FLAGS_TEST_FILESYS 0x0004 +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 + +#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 + +#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP +#define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT3_FEATURE_INCOMPAT_RECOVER| \ + EXT2_FEATURE_INCOMPAT_META_BG) +#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP + + + +/* + * Structure of the super block + */ +struct ext3_super_block { + /*00*/ __u32 s_inodes_count; + /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + /*10*/ __u32 s_free_inodes_count; + /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + /*20*/ __u32 s_blocks_per_group; + /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + /*30*/ __u32 s_wtime; + /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + /*40*/ __u32 s_lastcheck; + /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + /*50*/ __u16 s_def_resuid; + /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT3_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + /*60*/ __u32 s_feature_incompat; + /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + /*68*/ __u8 s_uuid[16]; + /* 128-bit uuid for volume */ + /*78*/ char s_volume_name[16]; + /* volume name */ + /*88*/ char s_last_mounted[64]; + /* directory where last mounted */ + /*C8*/ __u32 s_algorithm_usage_bitmap; + /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT3_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate */ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_padding1; + /* + * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set. + */ + /*D0*/ __u8 s_journal_uuid[16]; + /* uuid of journal superblock */ + /*E0*/ __u32 s_journal_inum; + /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; + __u16 s_reserved_word_pad; + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; + __u32 s_mkfs_time; + __u32 s_jnl_blocks[17]; + __u32 s_blocks_count_hi; + __u32 s_r_blocks_count_hi; + __u32 s_free_blocks_hi; + __u16 s_min_extra_isize; + __u16 s_want_extra_isize; + __u32 s_flags; + __u16 s_raid_stride; + __u16 s_mmp_interval; + __u64 s_mmp_block; + __u32 s_raid_stripe_width; + __u32 s_reserved[163]; +}; + +#endif /* __EXT3_FS_H */ diff --git a/usr/kinit/fstype/fstype.c b/usr/kinit/fstype/fstype.c new file mode 100644 index 0000000..aebccca --- /dev/null +++ b/usr/kinit/fstype/fstype.c @@ -0,0 +1,445 @@ +/* + * by rmk + * + * Detect filesystem type (on stdin) and output strings for two + * environment variables: + * FSTYPE - filesystem type + * FSSIZE - filesystem size (if known) + * + * We currently detect the filesystems listed below in the struct + * "imagetype images" (in the order they are listed). + */ + +#include <sys/types.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <endian.h> +#include <netinet/in.h> +#include <sys/utsname.h> +#include <sys/vfs.h> + +#define cpu_to_be32(x) __cpu_to_be32(x) /* Needed by romfs_fs.h */ + +#include "btrfs.h" +#include "cramfs_fs.h" +#include "ext2_fs.h" +#include "ext3_fs.h" +#include "gfs2_fs.h" +#include "iso9660_sb.h" +#include "luks_fs.h" +#include "lvm2_sb.h" +#include "minix_fs.h" +#include "nilfs_fs.h" +#include "ocfs2_fs.h" +#include "romfs_fs.h" +#include "squashfs_fs.h" +#include "xfs_sb.h" + +/* + * Slightly cleaned up version of jfs_superblock to + * avoid pulling in other kernel header files. + */ +#include "jfs_superblock.h" + +/* + * reiserfs_fs.h is too sick to include directly. + * Use a cleaned up version. + */ +#include "reiserfs_fs.h" +#include "reiser4_fs.h" + +#include "fstype.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define BLOCK_SIZE 1024 + +/* Swap needs the definition of block size */ +#include "swap_fs.h" + +static int gzip_image(const void *buf, unsigned long long *bytes) +{ + const unsigned char *p = buf; + + if (p[0] == 037 && (p[1] == 0213 || p[1] == 0236)) { + /* The length of a gzip stream can only be determined + by processing the whole stream */ + *bytes = 0ULL; + return 1; + } + return 0; +} + +static int cramfs_image(const void *buf, unsigned long long *bytes) +{ + const struct cramfs_super *sb = (const struct cramfs_super *)buf; + + if (sb->magic == CRAMFS_MAGIC) { + if (sb->flags & CRAMFS_FLAG_FSID_VERSION_2) + *bytes = (unsigned long long)sb->fsid.blocks << 10; + else + *bytes = 0; + return 1; + } + return 0; +} + +static int romfs_image(const void *buf, unsigned long long *bytes) +{ + const struct romfs_super_block *sb = + (const struct romfs_super_block *)buf; + + if (sb->word0 == ROMSB_WORD0 && sb->word1 == ROMSB_WORD1) { + *bytes = __be32_to_cpu(sb->size); + return 1; + } + return 0; +} + +static int minix_image(const void *buf, unsigned long long *bytes) +{ + const struct minix_super_block *sb = + (const struct minix_super_block *)buf; + + if (sb->s_magic == MINIX_SUPER_MAGIC || + sb->s_magic == MINIX_SUPER_MAGIC2) { + *bytes = (unsigned long long)sb->s_nzones + << (sb->s_log_zone_size + 10); + return 1; + } + return 0; +} + +static int ext4_image(const void *buf, unsigned long long *bytes) +{ + const struct ext3_super_block *sb = + (const struct ext3_super_block *)buf; + + if (sb->s_magic != __cpu_to_le16(EXT2_SUPER_MAGIC)) + return 0; + + /* There is at least one feature not supported by ext3 */ + if ((sb->s_feature_incompat + & __cpu_to_le32(EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) || + (sb->s_feature_ro_compat + & __cpu_to_le32(EXT3_FEATURE_RO_COMPAT_UNSUPPORTED))) { + *bytes = (unsigned long long)__le32_to_cpu(sb->s_blocks_count) + << (10 + __le32_to_cpu(sb->s_log_block_size)); + return 1; + } + return 0; +} + +static int ext3_image(const void *buf, unsigned long long *bytes) +{ + const struct ext3_super_block *sb = + (const struct ext3_super_block *)buf; + + if (sb->s_magic == __cpu_to_le16(EXT2_SUPER_MAGIC) && + sb-> + s_feature_compat & __cpu_to_le32(EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { + *bytes = (unsigned long long)__le32_to_cpu(sb->s_blocks_count) + << (10 + __le32_to_cpu(sb->s_log_block_size)); + return 1; + } + return 0; +} + +static int ext2_image(const void *buf, unsigned long long *bytes) +{ + const struct ext2_super_block *sb = + (const struct ext2_super_block *)buf; + + if (sb->s_magic == __cpu_to_le16(EXT2_SUPER_MAGIC)) { + *bytes = (unsigned long long)__le32_to_cpu(sb->s_blocks_count) + << (10 + __le32_to_cpu(sb->s_log_block_size)); + return 1; + } + return 0; +} + +static int reiserfs_image(const void *buf, unsigned long long *bytes) +{ + const struct reiserfs_super_block *sb = + (const struct reiserfs_super_block *)buf; + + if (memcmp(REISERFS_MAGIC(sb), REISERFS_SUPER_MAGIC_STRING, + sizeof(REISERFS_SUPER_MAGIC_STRING) - 1) == 0 || + memcmp(REISERFS_MAGIC(sb), REISER2FS_SUPER_MAGIC_STRING, + sizeof(REISER2FS_SUPER_MAGIC_STRING) - 1) == 0 || + memcmp(REISERFS_MAGIC(sb), REISER2FS_JR_SUPER_MAGIC_STRING, + sizeof(REISER2FS_JR_SUPER_MAGIC_STRING) - 1) == 0) { + *bytes = (unsigned long long)REISERFS_BLOCK_COUNT(sb) * + REISERFS_BLOCKSIZE(sb); + return 1; + } + return 0; +} + +static int reiser4_image(const void *buf, unsigned long long *bytes) +{ + const struct reiser4_master_sb *sb = + (const struct reiser4_master_sb *)buf; + + if (memcmp(sb->ms_magic, REISER4_SUPER_MAGIC_STRING, + sizeof(REISER4_SUPER_MAGIC_STRING) - 1) == 0) { + *bytes = (unsigned long long) __le32_to_cpu(sb->ms_format) * + __le32_to_cpu(sb->ms_blksize); + return 1; + } + return 0; +} + +static int xfs_image(const void *buf, unsigned long long *bytes) +{ + const struct xfs_sb *sb = (const struct xfs_sb *)buf; + + if (__be32_to_cpu(sb->sb_magicnum) == XFS_SB_MAGIC) { + *bytes = __be64_to_cpu(sb->sb_dblocks) * + __be32_to_cpu(sb->sb_blocksize); + return 1; + } + return 0; +} + +static int jfs_image(const void *buf, unsigned long long *bytes) +{ + const struct jfs_superblock *sb = (const struct jfs_superblock *)buf; + + if (!memcmp(sb->s_magic, JFS_MAGIC, 4)) { + *bytes = __le64_to_cpu(sb->s_size) + << __le16_to_cpu(sb->s_l2pbsize); + return 1; + } + return 0; +} + +static int luks_image(const void *buf, unsigned long long *blocks) +{ + const struct luks_partition_header *lph = + (const struct luks_partition_header *)buf; + + if (!memcmp(lph->magic, LUKS_MAGIC, LUKS_MAGIC_L)) { + /* FSSIZE is dictated by the underlying fs, not by LUKS */ + *blocks = 0; + return 1; + } + return 0; +} + +static int swap_image(const void *buf, unsigned long long *blocks) +{ + const struct swap_super_block *ssb = + (const struct swap_super_block *)buf; + + if (!memcmp(ssb->magic, SWAP_MAGIC_1, SWAP_MAGIC_L) || + !memcmp(ssb->magic, SWAP_MAGIC_2, SWAP_MAGIC_L)) { + *blocks = 0; + return 1; + } + return 0; +} + +static int suspend_image(const void *buf, unsigned long long *blocks) +{ + const struct swap_super_block *ssb = + (const struct swap_super_block *)buf; + + if (!memcmp(ssb->magic, SUSP_MAGIC_1, SUSP_MAGIC_L) || + !memcmp(ssb->magic, SUSP_MAGIC_2, SUSP_MAGIC_L) || + !memcmp(ssb->magic, SUSP_MAGIC_U, SUSP_MAGIC_L)) { + *blocks = 0; + return 1; + } + return 0; +} + +static int lvm2_image(const void *buf, unsigned long long *blocks) +{ + const struct lvm2_super_block *lsb; + int i; + + /* We must check every 512 byte sector */ + for (i = 0; i < BLOCK_SIZE; i += 0x200) { + lsb = (const struct lvm2_super_block *)(buf + i); + + if (!memcmp(lsb->magic, LVM2_MAGIC, LVM2_MAGIC_L) && + !memcmp(lsb->type, LVM2_TYPE, LVM2_TYPE_L)) { + /* This is just one of possibly many PV's */ + *blocks = 0; + return 1; + } + } + + return 0; +} + +static int iso_image(const void *buf, unsigned long long *blocks) +{ + const struct iso_volume_descriptor *isovd = + (const struct iso_volume_descriptor *)buf; + const struct iso_hs_volume_descriptor *isohsvd = + (const struct iso_hs_volume_descriptor *)buf; + + if (!memcmp(isovd->id, ISO_MAGIC, ISO_MAGIC_L) || + !memcmp(isohsvd->id, ISO_HS_MAGIC, ISO_HS_MAGIC_L)) { + *blocks = 0; + return 1; + } + return 0; +} + +static int squashfs_image(const void *buf, unsigned long long *blocks) +{ + const struct squashfs_super_block *sb = + (const struct squashfs_super_block *)buf; + + if (sb->s_magic == SQUASHFS_MAGIC + || sb->s_magic == SQUASHFS_MAGIC_SWAP + || sb->s_magic == SQUASHFS_MAGIC_LZMA + || sb->s_magic == SQUASHFS_MAGIC_LZMA_SWAP) { + *blocks = (unsigned long long) sb->bytes_used; + return 1; + } + return 0; +} + +static int gfs2_image(const void *buf, unsigned long long *bytes) +{ + const struct gfs2_sb *sb = + (const struct gfs2_sb *)buf; + + if (__be32_to_cpu(sb->sb_header.mh_magic) == GFS2_MAGIC + && (__be32_to_cpu(sb->sb_fs_format) == GFS2_FORMAT_FS + || __be32_to_cpu(sb->sb_fs_format) == GFS2_FORMAT_MULTI)) { + *bytes = 0; /* cpu_to_be32(sb->sb_bsize) * ?; */ + return 1; + } + return 0; +} + +static int ocfs2_image(const void *buf, unsigned long long *bytes) +{ + const struct ocfs2_dinode *sb = + (const struct ocfs2_dinode *)buf; + + if (!memcmp(sb->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE, + sizeof(OCFS2_SUPER_BLOCK_SIGNATURE) - 1)) { + *bytes = 0; + return 1; + } + return 0; +} + +static int nilfs2_image(const void *buf, unsigned long long *bytes) +{ + const struct nilfs_super_block *sb = + (const struct nilfs_super_block *)buf; + + if (sb->s_magic == __cpu_to_le16(NILFS_SUPER_MAGIC) && + sb->s_rev_level == __cpu_to_le32(2)) { + *bytes = (unsigned long long)__le64_to_cpu(sb->s_dev_size); + return 1; + } + return 0; +} + +static int btrfs_image(const void *buf, unsigned long long *bytes) +{ + const struct btrfs_super_block *sb = + (const struct btrfs_super_block *)buf; + + if (!memcmp(sb->magic, BTRFS_MAGIC, BTRFS_MAGIC_L)) { + *bytes = (unsigned long long)__le64_to_cpu(sb->total_bytes); + return 1; + } + return 0; +} + +struct imagetype { + off_t block; + const char name[12]; + int (*identify) (const void *, unsigned long long *); +}; + +/* + * Note: + * + * Minix test needs to come after ext3/ext2, since it's possible for + * ext3/ext2 to look like minix by pure random chance. + * + * LVM comes after all other filesystems since it's possible + * that an old lvm signature is left on the disk if pvremove + * is not used before creating the new fs. + * + * The same goes for LUKS as for LVM. + */ +static struct imagetype images[] = { + {0, "gzip", gzip_image}, + {0, "cramfs", cramfs_image}, + {0, "romfs", romfs_image}, + {0, "xfs", xfs_image}, + {0, "squashfs", squashfs_image}, + {1, "ext4", ext4_image}, + {1, "ext3", ext3_image}, + {1, "ext2", ext2_image}, + {1, "minix", minix_image}, + {1, "nilfs2", nilfs2_image}, + {2, "ocfs2", ocfs2_image}, + {8, "reiserfs", reiserfs_image}, + {64, "reiserfs", reiserfs_image}, + {64, "reiser4", reiser4_image}, + {64, "gfs2", gfs2_image}, + {64, "btrfs", btrfs_image}, + {32, "jfs", jfs_image}, + {32, "iso9660", iso_image}, + {0, "luks", luks_image}, + {0, "lvm2", lvm2_image}, + {1, "lvm2", lvm2_image}, + {-1, "swap", swap_image}, + {-1, "suspend", suspend_image}, + {0, "", NULL} +}; + +int identify_fs(int fd, const char **fstype, + unsigned long long *bytes, off_t offset) +{ + uint64_t buf[BLOCK_SIZE >> 3]; /* 64-bit worst case alignment */ + off_t cur_block = (off_t) -1; + struct imagetype *ip; + int ret; + unsigned long long dummy; + + if (!bytes) + bytes = &dummy; + + *fstype = NULL; + *bytes = 0; + + for (ip = images; ip->identify; ip++) { + /* Hack for swap, which apparently is dependent on page size */ + if (ip->block == -1) + ip->block = SWAP_OFFSET(); + + if (cur_block != ip->block) { + /* + * Read block. + */ + cur_block = ip->block; + ret = pread(fd, buf, BLOCK_SIZE, + offset + cur_block * BLOCK_SIZE); + if (ret != BLOCK_SIZE) + return -1; /* error */ + } + + if (ip->identify(buf, bytes)) { + *fstype = ip->name; + return 0; + } + } + + return 1; /* Unknown filesystem */ +} diff --git a/usr/kinit/fstype/fstype.h b/usr/kinit/fstype/fstype.h new file mode 100644 index 0000000..be2a3e4 --- /dev/null +++ b/usr/kinit/fstype/fstype.h @@ -0,0 +1,20 @@ +/* + * by rmk + * + * Detect filesystem type (on stdin) and output strings for two + * environment variables: + * FSTYPE - filesystem type + * FSSIZE - filesystem size (if known) + * + * We currently detect the fs listed in struct imagetype. + */ + +#ifndef FSTYPE_H +#define FSTYPE_H + +#include <unistd.h> + +int identify_fs(int fd, const char **fstype, + unsigned long long *bytes, off_t offset); + +#endif diff --git a/usr/kinit/fstype/gfs2_fs.h b/usr/kinit/fstype/gfs2_fs.h new file mode 100644 index 0000000..028e0c9 --- /dev/null +++ b/usr/kinit/fstype/gfs2_fs.h @@ -0,0 +1,56 @@ +#ifndef __GFS2_FS_H +#define __GFS2_FS_H + +#define GFS2_MAGIC 0x01161970 +#define GFS2_FORMAT_FS 1801 +#define GFS2_FORMAT_MULTI 1900 + + +/* + * An on-disk inode number + */ +struct gfs2_inum { + __be64 no_formal_ino; + __be64 no_addr; +}; + +/* + * Generic metadata head structure + * Every inplace buffer logged in the journal must start with this. + */ +struct gfs2_meta_header { + uint32_t mh_magic; + uint32_t mh_type; + uint64_t __pad0; /* Was generation number in gfs1 */ + uint32_t mh_format; + uint32_t __pad1; /* Was incarnation number in gfs1 */ +}; + +/* Requirement: GFS2_LOCKNAME_LEN % 8 == 0 + * Includes: the fencing zero at the end */ +#define GFS2_LOCKNAME_LEN 64 + +/* + * super-block structure + */ +struct gfs2_sb { + struct gfs2_meta_header sb_header; + + uint32_t sb_fs_format; + uint32_t sb_multihost_format; + uint32_t __pad0; /* Was superblock flags in gfs1 */ + + uint32_t sb_bsize; + uint32_t sb_bsize_shift; + uint32_t __pad1; /* Was journal segment size in gfs1 */ + + struct gfs2_inum sb_master_dir; /* Was jindex dinode in gfs1 */ + struct gfs2_inum __pad2; /* Was rindex dinode in gfs1 */ + struct gfs2_inum sb_root_dir; + + char sb_lockproto[GFS2_LOCKNAME_LEN]; + char sb_locktable[GFS2_LOCKNAME_LEN]; + /* In gfs1, quota and license dinodes followed */ +} __attribute__ ((__packed__)); + +#endif /* __GFS2_FS_H */ diff --git a/usr/kinit/fstype/iso9660_sb.h b/usr/kinit/fstype/iso9660_sb.h new file mode 100644 index 0000000..efe0733 --- /dev/null +++ b/usr/kinit/fstype/iso9660_sb.h @@ -0,0 +1,24 @@ +#ifndef __ISO9660_SB_H +#define __ISO9660_SB_H + +#define ISO_MAGIC_L 5 +#define ISO_MAGIC "CD001" +#define ISO_HS_MAGIC_L 5 +#define ISO_HS_MAGIC "CDROM" + +/* ISO9660 Volume Descriptor */ +struct iso_volume_descriptor { + __u8 type; + char id[ISO_MAGIC_L]; + __u8 version; +}; + +/* High Sierra Volume Descriptor */ +struct iso_hs_volume_descriptor { + char foo[8]; + __u8 type; + char id[ISO_HS_MAGIC_L]; + __u8 version; +}; + +#endif diff --git a/usr/kinit/fstype/jfs_superblock.h b/usr/kinit/fstype/jfs_superblock.h new file mode 100644 index 0000000..63132a0 --- /dev/null +++ b/usr/kinit/fstype/jfs_superblock.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) International Business Machines Corp., 2000-2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _H_JFS_SUPERBLOCK +#define _H_JFS_SUPERBLOCK + +struct timestruc_t { + __le32 tv_sec; + __le32 tv_nsec; +}; + +/* + * make the magic number something a human could read + */ +#define JFS_MAGIC "JFS1" /* Magic word */ + +#define JFS_VERSION 2 /* Version number: Version 2 */ + +#define LV_NAME_SIZE 11 /* MUST BE 11 for OS/2 boot sector */ + +/* + * aggregate superblock + * + * The name superblock is too close to super_block, so the name has been + * changed to jfs_superblock. The utilities are still using the old name. + */ +struct jfs_superblock { + char s_magic[4]; /* 4: magic number */ + __le32 s_version; /* 4: version number */ + + __le64 s_size; /* 8: aggregate size in hardware/LVM blocks; + * VFS: number of blocks + */ + __le32 s_bsize; /* 4: aggregate block size in bytes; + * VFS: fragment size + */ + __le16 s_l2bsize; /* 2: log2 of s_bsize */ + __le16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ + __le32 s_pbsize; /* 4: hardware/LVM block size in bytes */ + __le16 s_l2pbsize; /* 2: log2 of s_pbsize */ + __le16 pad; /* 2: padding necessary for alignment */ + + __le32 s_agsize; /* 4: allocation group size in aggr. blocks */ + + __le32 s_flag; /* 4: aggregate attributes: + * see jfs_filsys.h + */ + __le32 s_state; /* 4: mount/unmount/recovery state: + * see jfs_filsys.h + */ + __le32 s_compress; /* 4: > 0 if data compression */ + + __le64 s_ait2; /* 8: first extent of secondary + * aggregate inode table + */ + + __le64 s_aim2; /* 8: first extent of secondary + * aggregate inode map + */ + __le32 s_logdev; /* 4: device address of log */ + __le32 s_logserial; /* 4: log serial number at aggregate mount */ + __le64 s_logpxd; /* 8: inline log extent */ + + __le64 s_fsckpxd; /* 8: inline fsck work space extent */ + + struct timestruc_t s_time; /* 8: time last updated */ + + __le32 s_fsckloglen; /* 4: Number of filesystem blocks reserved for + * the fsck service log. + * N.B. These blocks are divided among the + * versions kept. This is not a per + * version size. + * N.B. These blocks are included in the + * length field of s_fsckpxd. + */ + char s_fscklog; /* 1: which fsck service log is most recent + * 0 => no service log data yet + * 1 => the first one + * 2 => the 2nd one + */ + char s_fpack[11]; /* 11: file system volume name + * N.B. This must be 11 bytes to + * conform with the OS/2 BootSector + * requirements + * Only used when s_version is 1 + */ + + /* extendfs() parameter under s_state & FM_EXTENDFS */ + __le64 s_xsize; /* 8: extendfs s_size */ + __le64 s_xfsckpxd; /* 8: extendfs fsckpxd */ + __le64 s_xlogpxd; /* 8: extendfs logpxd */ + /* - 128 byte boundary - */ + + char s_uuid[16]; /* 16: 128-bit uuid for volume */ + char s_label[16]; /* 16: volume label */ + char s_loguuid[16]; /* 16: 128-bit uuid for log device */ + +}; + +#endif /*_H_JFS_SUPERBLOCK */ diff --git a/usr/kinit/fstype/luks_fs.h b/usr/kinit/fstype/luks_fs.h new file mode 100644 index 0000000..fd8de31 --- /dev/null +++ b/usr/kinit/fstype/luks_fs.h @@ -0,0 +1,44 @@ +#ifndef __LINUX_LUKS_FS_H +#define __LINUX_LUKS_FS_H + +/* The basic structures of the luks partition header */ +#define LUKS_MAGIC_L 6 +#define LUKS_CIPHERNAME_L 32 +#define LUKS_CIPHERMODE_L 32 +#define LUKS_HASHSPEC_L 32 +#define LUKS_UUID_STRING_L 40 + +#define LUKS_MAGIC "LUKS\xBA\xBE" +#define LUKS_DIGESTSIZE 20 +#define LUKS_SALTSIZE 32 +#define LUKS_NUMKEYS 8 +#define LUKS_MKD_ITER 10 +#define LUKS_KEY_DISABLED 0x0000DEAD +#define LUKS_KEY_ENABLED 0x00AC71F3 +#define LUKS_STRIPES 4000 + +/* On-disk "super block" */ +struct luks_partition_header { + char magic[LUKS_MAGIC_L]; + __be16 version; + char cipherName[LUKS_CIPHERNAME_L]; + char cipherMode[LUKS_CIPHERMODE_L]; + char hashSpec[LUKS_HASHSPEC_L]; + __be32 payloadOffset; + __be32 keyBytes; + char mkDigest[LUKS_DIGESTSIZE]; + char mkDigestSalt[LUKS_SALTSIZE]; + __be32 mkDigestIterations; + char uuid[LUKS_UUID_STRING_L]; + + struct { + __be32 active; + /* Parameters for PBKDF2 processing */ + __be32 passwordIterations; + char passwordSalt[LUKS_SALTSIZE]; + __be32 keyMaterialOffset; + __be32 stripes; + } keyblock[LUKS_NUMKEYS]; +}; + +#endif diff --git a/usr/kinit/fstype/lvm2_sb.h b/usr/kinit/fstype/lvm2_sb.h new file mode 100644 index 0000000..75dfc10 --- /dev/null +++ b/usr/kinit/fstype/lvm2_sb.h @@ -0,0 +1,18 @@ +#ifndef __LVM2_SB_H +#define __LVM2_SB_H + +/* LVM2 super block definitions */ +#define LVM2_MAGIC_L 8 +#define LVM2_MAGIC "LABELONE" +#define LVM2_TYPE_L 8 +#define LVM2_TYPE "LVM2 001" + +struct lvm2_super_block { + char magic[LVM2_MAGIC_L]; + __be64 sector; + __be32 crc; + __be32 offset; + char type[LVM2_TYPE_L]; +}; + +#endif diff --git a/usr/kinit/fstype/main.c b/usr/kinit/fstype/main.c new file mode 100644 index 0000000..9162bdf --- /dev/null +++ b/usr/kinit/fstype/main.c @@ -0,0 +1,57 @@ +/* + * by rmk + * + * Detect filesystem type (on stdin) and output strings for two + * environment variables: + * FSTYPE - filesystem type + * FSSIZE - filesystem size (if known) + * + * We currently detect (in order): + * gzip, cramfs, romfs, xfs, minix, ext3, ext2, reiserfs, jfs + * + * MINIX, ext3 and Reiserfs bits are currently untested. + */ + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include "fstype.h" + +char *progname; + +int main(int argc, char *argv[]) +{ + int fd = 0; + int rv; + const char *fstype; + const char *file = "stdin"; + unsigned long long bytes; + + progname = argv[0]; + + if (argc > 2) { + fprintf(stderr, "Usage: %s [file]\n", progname); + return 1; + } + + if (argc > 1 && !(argv[1][0] == '-' && argv[1][1] == '\0')) { + fd = open(file = argv[1], O_RDONLY); + if (fd < 0) { + perror(argv[1]); + return 2; + } + } + + rv = identify_fs(fd, &fstype, &bytes, 0); + if (rv == -1) { + perror(file); + return 2; + } + + fstype = fstype ? fstype : "unknown"; + + fprintf(stdout, "FSTYPE=%s\nFSSIZE=%llu\n", fstype, bytes); + return rv; +} diff --git a/usr/kinit/fstype/minix_fs.h b/usr/kinit/fstype/minix_fs.h new file mode 100644 index 0000000..e2899f0 --- /dev/null +++ b/usr/kinit/fstype/minix_fs.h @@ -0,0 +1,85 @@ +#ifndef _LINUX_MINIX_FS_H +#define _LINUX_MINIX_FS_H + +/* + * The minix filesystem constants/structures + */ + +/* + * Thanks to Kees J Bot for sending me the definitions of the new + * minix filesystem (aka V2) with bigger inodes and 32-bit block + * pointers. + */ + +#define MINIX_ROOT_INO 1 + +/* Not the same as the bogus LINK_MAX in <linux/limits.h>. Oh well. */ +#define MINIX_LINK_MAX 250 +#define MINIX2_LINK_MAX 65530 + +#define MINIX_I_MAP_SLOTS 8 +#define MINIX_Z_MAP_SLOTS 64 +#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */ +#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */ +#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */ +#define MINIX_VALID_FS 0x0001 /* Clean fs. */ +#define MINIX_ERROR_FS 0x0002 /* fs has errors. */ + +#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode))) +#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode))) + +/* + * This is the original minix inode layout on disk. + * Note the 8-bit gid and atime and ctime. + */ +struct minix_inode { + __u16 i_mode; + __u16 i_uid; + __u32 i_size; + __u32 i_time; + __u8 i_gid; + __u8 i_nlinks; + __u16 i_zone[9]; +}; + +/* + * The new minix inode has all the time entries, as well as + * long block numbers and a third indirect block (7+1+1+1 + * instead of 7+1+1). Also, some previously 8-bit values are + * now 16-bit. The inode is now 64 bytes instead of 32. + */ +struct minix2_inode { + __u16 i_mode; + __u16 i_nlinks; + __u16 i_uid; + __u16 i_gid; + __u32 i_size; + __u32 i_atime; + __u32 i_mtime; + __u32 i_ctime; + __u32 i_zone[10]; +}; + +/* + * minix super-block data on disk + */ +struct minix_super_block { + __u16 s_ninodes; + __u16 s_nzones; + __u16 s_imap_blocks; + __u16 s_zmap_blocks; + __u16 s_firstdatazone; + __u16 s_log_zone_size; + __u32 s_max_size; + __u16 s_magic; + __u16 s_state; + __u32 s_zones; +}; + +struct minix_dir_entry { + __u16 inode; + char name[0]; +}; + +#endif diff --git a/usr/kinit/fstype/nilfs_fs.h b/usr/kinit/fstype/nilfs_fs.h new file mode 100644 index 0000000..0845edf --- /dev/null +++ b/usr/kinit/fstype/nilfs_fs.h @@ -0,0 +1,64 @@ +#ifndef __NILFS_FS_H +#define __NILFS_FS_H + +#define NILFS_SUPER_MAGIC 0x3434 /* NILFS filesystem magic number */ + +/* + * struct nilfs_super_block - structure of super block on disk + */ +struct nilfs_super_block { + __le32 s_rev_level; /* Revision level */ + __le16 s_minor_rev_level; /* minor revision level */ + __le16 s_magic; /* Magic signature */ + + __le16 s_bytes; /* Bytes count of CRC calculation + for this structure. s_reserved + is excluded. */ + __le16 s_flags; /* flags */ + __le32 s_crc_seed; /* Seed value of CRC calculation */ + __le32 s_sum; /* Check sum of super block */ + + __le32 s_log_block_size; /* Block size represented as follows + blocksize = 1 << (s_log_block_size + 10) */ + __le64 s_nsegments; /* Number of segments in filesystem */ + __le64 s_dev_size; /* block device size in bytes */ + __le64 s_first_data_block; /* 1st seg disk block number */ + __le32 s_blocks_per_segment; /* number of blocks per full segment */ + __le32 s_r_segments_percentage;/* Reserved segments percentage */ /* or __le16 */ + + __le64 s_last_cno; /* Last checkpoint number */ + __le64 s_last_pseg; /* disk block addr pseg written last */ + __le64 s_last_seq; /* seq. number of seg written last */ + __le64 s_free_blocks_count; /* Free blocks count */ + + __le64 s_ctime; /* Creation time (execution time of newfs) */ + __le64 s_mtime; /* Mount time */ + __le64 s_wtime; /* Write time */ + __le16 s_mnt_count; /* Mount count */ + __le16 s_max_mnt_count; /* Maximal mount count */ + __le16 s_state; /* File system state */ + __le16 s_errors; /* Behaviour when detecting errors */ + __le64 s_lastcheck; /* time of last check */ + + __le32 s_checkinterval; /* max. time between checks */ + __le32 s_creator_os; /* OS */ + __le16 s_def_resuid; /* Default uid for reserved blocks */ + __le16 s_def_resgid; /* Default gid for reserved blocks */ + __le32 s_first_ino; /* First non-reserved inode */ /* or __le16 */ + + __le16 s_inode_size; /* Size of an inode */ + __le16 s_dat_entry_size; /* Size of a dat entry */ + __le16 s_checkpoint_size; /* Size of a checkpoint */ + __le16 s_segment_usage_size; /* Size of a segment usage */ + + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + + __le32 s_c_interval; /* Commit interval of segment */ + __le32 s_c_block_max; /* Threshold of data amount for + the segment construction */ + __u32 s_reserved[192]; /* padding to the end of the block */ +}; + +#endif /* __NILFS_FS_H */ diff --git a/usr/kinit/fstype/ocfs2_fs.h b/usr/kinit/fstype/ocfs2_fs.h new file mode 100644 index 0000000..b71cb61 --- /dev/null +++ b/usr/kinit/fstype/ocfs2_fs.h @@ -0,0 +1,90 @@ +#ifndef _OCFS2_FS_H +#define _OCFS2_FS_H + +/* Object signatures */ +#define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2" + +#define OCFS2_VOL_UUID_LEN 16 +#define OCFS2_MAX_VOL_LABEL_LEN 64 + +/* + * On disk superblock for OCFS2 + * Note that it is contained inside an ocfs2_dinode, so all offsets + * are relative to the start of ocfs2_dinode.id2. + */ +struct ocfs2_super_block { +/*00*/ uint16_t s_major_rev_level; + uint16_t s_minor_rev_level; + uint16_t s_mnt_count; + int16_t s_max_mnt_count; + uint16_t s_state; /* File system state */ + uint16_t s_errors; /* Behaviour when detecting errors */ + uint32_t s_checkinterval; /* Max time between checks */ +/*10*/ uint64_t s_lastcheck; /* Time of last check */ + uint32_t s_creator_os; /* OS */ + uint32_t s_feature_compat; /* Compatible feature set */ +/*20*/ uint32_t s_feature_incompat; /* Incompatible feature set */ + uint32_t s_feature_ro_compat; /* Readonly-compatible feature set */ + uint64_t s_root_blkno; /* Offset, in blocks, of root directory + dinode */ +/*30*/ uint64_t s_system_dir_blkno; /* Offset, in blocks, of system + directory dinode */ + uint32_t s_blocksize_bits; /* Blocksize for this fs */ + uint32_t s_clustersize_bits; /* Clustersize for this fs */ +/*40*/ uint16_t s_max_slots; /* Max number of simultaneous mounts + before tunefs required */ + uint16_t s_reserved1; + uint32_t s_reserved2; + uint64_t s_first_cluster_group; /* Block offset of 1st cluster + * group header */ +/*50*/ uint8_t s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ +/*90*/ uint8_t s_uuid[OCFS2_VOL_UUID_LEN]; /* 128-bit uuid */ +/*A0*/ +} __attribute__ ((packed)); + +/* + * On disk inode for OCFS2 + */ +struct ocfs2_dinode { +/*00*/ uint8_t i_signature[8]; /* Signature for validation */ + uint32_t i_generation; /* Generation number */ + uint16_t i_suballoc_slot; /* Slot suballocator this inode + belongs to */ + int16_t i_suballoc_bit; /* Bit offset in suballocator + block group */ +/*10*/ uint32_t i_reserved0; + uint32_t i_clusters; /* Cluster count */ + uint32_t i_uid; /* Owner UID */ + uint32_t i_gid; /* Owning GID */ +/*20*/ uint64_t i_size; /* Size in bytes */ + uint16_t i_mode; /* File mode */ + uint16_t i_links_count; /* Links count */ + uint32_t i_flags; /* File flags */ +/*30*/ uint64_t i_atime; /* Access time */ + uint64_t i_ctime; /* Creation time */ +/*40*/ uint64_t i_mtime; /* Modification time */ + uint64_t i_dtime; /* Deletion time */ +/*50*/ uint64_t i_blkno; /* Offset on disk, in blocks */ + uint64_t i_last_eb_blk; /* Pointer to last extent + block */ +/*60*/ uint32_t i_fs_generation; /* Generation per fs-instance */ + uint32_t i_atime_nsec; + uint32_t i_ctime_nsec; + uint32_t i_mtime_nsec; + uint32_t i_attr; + uint16_t i_orphaned_slot; /* Only valid when OCFS2_ORPHANED_FL + was set in i_flags */ + uint16_t i_reserved1; +/*70*/ uint64_t i_reserved2[8]; +/*B8*/ uint64_t i_pad1; + uint64_t i_rdev; /* Device number */ + uint32_t i_used; /* Bits (ie, clusters) used */ + uint32_t i_total; /* Total bits (clusters) + available */ + uint32_t ij_flags; /* Mounted, version, etc. */ + uint32_t ij_pad; +/*C0*/ struct ocfs2_super_block i_super; +/* Actual on-disk size is one block */ +} __attribute__ ((packed)); + +#endif /* _OCFS2_FS_H */ diff --git a/usr/kinit/fstype/reiser4_fs.h b/usr/kinit/fstype/reiser4_fs.h new file mode 100644 index 0000000..af6ccc4 --- /dev/null +++ b/usr/kinit/fstype/reiser4_fs.h @@ -0,0 +1,31 @@ +#ifndef __REISER4_FS_H +#define __REISER4_FS_H + +#define SS_MAGIC_SIZE 16 + +/* reiser4 filesystem structure + * + * Master super block structure. It is the same for all reiser4 filesystems, + * so, we can declare it here. It contains common for all format fields like + * block size etc. + */ +struct reiser4_master_sb { + /* Master super block magic. */ + char ms_magic[SS_MAGIC_SIZE]; + + /* Disk format in use. */ + __u16 ms_format; + + /* Filesyetem block size in use. */ + __u16 ms_blksize; + + /* Filesyetm uuid in use. */ + char ms_uuid[SS_MAGIC_SIZE]; + + /* Filesystem label in use. */ + char ms_label[SS_MAGIC_SIZE]; +} __attribute__ ((packed)); + +#define REISER4_SUPER_MAGIC_STRING "ReIsEr4" + +#endif /* __REISER4_FS_H */ diff --git a/usr/kinit/fstype/reiserfs_fs.h b/usr/kinit/fstype/reiserfs_fs.h new file mode 100644 index 0000000..096d505 --- /dev/null +++ b/usr/kinit/fstype/reiserfs_fs.h @@ -0,0 +1,74 @@ +#ifndef __REISERFS_FS_H +#define __REISERFS_FS_H + +struct journal_params { + __u32 jp_journal_1st_block; /* where does journal start from on its + * device */ + __u32 jp_journal_dev; /* journal device st_rdev */ + __u32 jp_journal_size; /* size of the journal */ + __u32 jp_journal_trans_max; /* max number of blocks in a transaction. */ + __u32 jp_journal_magic; /* random value made on fs creation (this + * was sb_journal_block_count) */ + __u32 jp_journal_max_batch; /* max number of blocks to batch into a + * trans */ + __u32 jp_journal_max_commit_age; /* in seconds, how old can an async + * commit be */ + __u32 jp_journal_max_trans_age; /* in seconds, how old can a transaction + * be */ +}; + +/* this is the super from 3.5.X, where X >= 10 */ +struct reiserfs_super_block_v1 { + __u32 s_block_count; /* blocks count */ + __u32 s_free_blocks; /* free blocks count */ + __u32 s_root_block; /* root block number */ + struct journal_params s_journal; + __u16 s_blocksize; /* block size */ + __u16 s_oid_maxsize; /* max size of object id array, see + * get_objectid() commentary */ + __u16 s_oid_cursize; /* current size of object id array */ + __u16 s_umount_state; /* this is set to 1 when filesystem was + * umounted, to 2 - when not */ + char s_magic[10]; /* reiserfs magic string indicates that + * file system is reiserfs: + * "ReIsErFs" or "ReIsEr2Fs" or "ReIsEr3Fs" */ + __u16 s_fs_state; /* it is set to used by fsck to mark which + * phase of rebuilding is done */ + __u32 s_hash_function_code; /* indicate, what hash function is being use + * to sort names in a directory*/ + __u16 s_tree_height; /* height of disk tree */ + __u16 s_bmap_nr; /* amount of bitmap blocks needed to address + * each block of file system */ + __u16 s_version; /* this field is only reliable on filesystem + * with non-standard journal */ + __u16 s_reserved_for_journal; /* size in blocks of journal area on main + * device, we need to keep after + * making fs with non-standard journal */ +} __attribute__ ((__packed__)); + +/* this is the on disk super block */ +struct reiserfs_super_block { + struct reiserfs_super_block_v1 s_v1; + __u32 s_inode_generation; + __u32 s_flags; /* Right now used only by inode-attributes, if enabled */ + unsigned char s_uuid[16]; /* filesystem unique identifier */ + unsigned char s_label[16]; /* filesystem volume label */ + char s_unused[88]; /* zero filled by mkreiserfs and + * reiserfs_convert_objectid_map_v1() + * so any additions must be updated + * there as well. */ +} __attribute__ ((__packed__)); + +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" +#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" + +#define SB_V1_DISK_SUPER_BLOCK(s) (&((s)->s_v1)) +#define REISERFS_BLOCKSIZE(s) \ + __le32_to_cpu((SB_V1_DISK_SUPER_BLOCK(s)->s_blocksize)) +#define REISERFS_BLOCK_COUNT(s) \ + __le32_to_cpu((SB_V1_DISK_SUPER_BLOCK(s)->s_block_count)) +#define REISERFS_MAGIC(s) \ + (SB_V1_DISK_SUPER_BLOCK(s)->s_magic) + +#endif /* __REISERFS_FS_H */ diff --git a/usr/kinit/fstype/romfs_fs.h b/usr/kinit/fstype/romfs_fs.h new file mode 100644 index 0000000..c490fbc --- /dev/null +++ b/usr/kinit/fstype/romfs_fs.h @@ -0,0 +1,56 @@ +#ifndef __LINUX_ROMFS_FS_H +#define __LINUX_ROMFS_FS_H + +/* The basic structures of the romfs filesystem */ + +#define ROMBSIZE BLOCK_SIZE +#define ROMBSBITS BLOCK_SIZE_BITS +#define ROMBMASK (ROMBSIZE-1) +#define ROMFS_MAGIC 0x7275 + +#define ROMFS_MAXFN 128 + +#define __mkw(h,l) (((h)&0x00ff)<< 8|((l)&0x00ff)) +#define __mkl(h,l) (((h)&0xffff)<<16|((l)&0xffff)) +#define __mk4(a,b,c,d) cpu_to_be32(__mkl(__mkw(a,b),__mkw(c,d))) +#define ROMSB_WORD0 __mk4('-','r','o','m') +#define ROMSB_WORD1 __mk4('1','f','s','-') + +/* On-disk "super block" */ + +struct romfs_super_block { + __be32 word0; + __be32 word1; + __be32 size; + __be32 checksum; + char name[0]; /* volume name */ +}; + +/* On disk inode */ + +struct romfs_inode { + __be32 next; /* low 4 bits see ROMFH_ */ + __be32 spec; + __be32 size; + __be32 checksum; + char name[0]; +}; + +#define ROMFH_TYPE 7 +#define ROMFH_HRD 0 +#define ROMFH_DIR 1 +#define ROMFH_REG 2 +#define ROMFH_SYM 3 +#define ROMFH_BLK 4 +#define ROMFH_CHR 5 +#define ROMFH_SCK 6 +#define ROMFH_FIF 7 +#define ROMFH_EXEC 8 + +/* Alignment */ + +#define ROMFH_SIZE 16 +#define ROMFH_PAD (ROMFH_SIZE-1) +#define ROMFH_MASK (~ROMFH_PAD) + +#endif diff --git a/usr/kinit/fstype/squashfs_fs.h b/usr/kinit/fstype/squashfs_fs.h new file mode 100644 index 0000000..c18365d --- /dev/null +++ b/usr/kinit/fstype/squashfs_fs.h @@ -0,0 +1,48 @@ +#ifndef __SQUASHFS_FS_H +#define __SQUASHFS_FS_H + +/* + * Squashfs + */ + +#define SQUASHFS_MAGIC 0x73717368 +#define SQUASHFS_MAGIC_SWAP 0x68737173 + +/* + * Squashfs + LZMA + */ + +#define SQUASHFS_MAGIC_LZMA 0x71736873 +#define SQUASHFS_MAGIC_LZMA_SWAP 0x73687371 + +/* definitions for structures on disk */ +struct squashfs_super_block { + unsigned int s_magic; + unsigned int inodes; + unsigned int bytes_used_2; + unsigned int uid_start_2; + unsigned int guid_start_2; + unsigned int inode_table_start_2; + unsigned int directory_table_start_2; + unsigned int s_major:16; + unsigned int s_minor:16; + unsigned int block_size_1:16; + unsigned int block_log:16; + unsigned int flags:8; + unsigned int no_uids:8; + unsigned int no_guids:8; + unsigned int mkfs_time /* time of filesystem creation */; + long long root_inode; + unsigned int block_size; + unsigned int fragments; + unsigned int fragment_table_start_2; + long long bytes_used; + long long uid_start; + long long guid_start; + long long inode_table_start; + long long directory_table_start; + long long fragment_table_start; + long long lookup_table_start; +} __attribute__ ((packed)); + +#endif /* __SQUASHFS_FS_H */ diff --git a/usr/kinit/fstype/swap_fs.h b/usr/kinit/fstype/swap_fs.h new file mode 100644 index 0000000..7b7fddb --- /dev/null +++ b/usr/kinit/fstype/swap_fs.h @@ -0,0 +1,25 @@ +#ifndef __LINUX_SWAP_FS_H +#define __LINUX_SWAP_FS_H + +/* The basic structures of the swap super block */ +#define SWAP_MAGIC_L 10 +#define SWAP_RESERVED_L (1024 - SWAP_MAGIC_L) +#define SWAP_MAGIC_1 "SWAP-SPACE" +#define SWAP_MAGIC_2 "SWAPSPACE2" + +/* Suspend signatures, located at same addr as swap magic */ +#define SUSP_MAGIC_L 9 +#define SUSP_MAGIC_1 "S1SUSPEND" +#define SUSP_MAGIC_2 "S2SUSPEND" +#define SUSP_MAGIC_U "ULSUSPEND" + +/* The superblock is the last block in the first page */ +#define SWAP_OFFSET() ((getpagesize() - 1024) >> 10) + +/* On-disk "super block" */ +struct swap_super_block { + char reserved[SWAP_RESERVED_L]; + char magic[SWAP_MAGIC_L]; +}; + +#endif diff --git a/usr/kinit/fstype/xfs_sb.h b/usr/kinit/fstype/xfs_sb.h new file mode 100644 index 0000000..fd54bc4 --- /dev/null +++ b/usr/kinit/fstype/xfs_sb.h @@ -0,0 +1,21 @@ +#ifndef __XFS_SB_H +#define __XFS_SB_H + +/* + * Super block + * Fits into a sector-sized buffer at address 0 of each allocation group. + * Only the first of these is ever updated except during growfs. + */ + +struct xfs_buf; +struct xfs_mount; + +#define XFS_SB_MAGIC 0x58465342 /* 'XFSB' */ + +typedef struct xfs_sb { + __u32 sb_magicnum; /* magic number == XFS_SB_MAGIC */ + __u32 sb_blocksize; /* logical block size, bytes */ + __u64 sb_dblocks; /* number of data blocks */ +} xfs_sb_t; + +#endif /* __XFS_SB_H */ diff --git a/usr/kinit/getarg.c b/usr/kinit/getarg.c new file mode 100644 index 0000000..fcce247 --- /dev/null +++ b/usr/kinit/getarg.c @@ -0,0 +1,57 @@ +#include <string.h> +#include "kinit.h" + +/* + * Routines that hunt for a specific argument. Please note that + * they actually search the array backwards. That is because on the + * kernel command lines, it's legal to override an earlier argument + * with a later argument. + */ + +/* + * Was this boolean argument passed? If so return the index in the + * argv array for it. For conflicting boolean options, use the + * one with the higher index. The only case when the return value + * can be equal, is when they're both zero; so equality can be used + * as the default option choice. + * + * In other words, if two options "a" and "b" are opposites, and "a" + * is the default, this can be coded as: + * + * if (get_flag(argc,argv,"a") >= get_flag(argc,argv,"b")) + * do_a_stuff(); + * else + * do_b_stuff(); + */ +int get_flag(int argc, char *argv[], const char *name) +{ + int i; + + for (i = argc-1; i > 0; i--) { + if (!strcmp(argv[i], name)) + return i; + } + return 0; +} + +/* + * Was this textual parameter (foo=option) passed? + * + * This returns the latest instance of such an option in the argv array. + */ +char *get_arg(int argc, char *argv[], const char *name) +{ + int len = strlen(name); + char *ret = NULL; + int i; + + for (i = argc-1; i > 0; i--) { + if (argv[i] && strncmp(argv[i], name, len) == 0 && + (argv[i][len] != '\0')) { + ret = argv[i] + len; + break; + } + } + + return ret; +} diff --git a/usr/kinit/getintfile.c b/usr/kinit/getintfile.c new file mode 100644 index 0000000..41ba475 --- /dev/null +++ b/usr/kinit/getintfile.c @@ -0,0 +1,30 @@ +/* + * Open a file and read it, assuming it contains a single long value. + * Return 0 if we read a valid value, otherwise -1. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "kinit.h" + +int getintfile(const char *path, long *val) +{ + char buffer[64]; + char *ep; + FILE *f; + + f = fopen(path, "r"); + if (!f) + return -1; + + ep = buffer + fread(buffer, 1, sizeof buffer - 1, f); + fclose(f); + *ep = '\0'; + + *val = strtol(buffer, &ep, 0); + if (*ep && *ep != '\n') + return -1; + else + return 0; +} diff --git a/usr/kinit/initrd.c b/usr/kinit/initrd.c new file mode 100644 index 0000000..5833f2f --- /dev/null +++ b/usr/kinit/initrd.c @@ -0,0 +1,204 @@ +/* + * Handle initrd, thus putting the backwards into backwards compatible + */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include "do_mounts.h" +#include "kinit.h" +#include "xpio.h" + +#define BUF_SIZE 65536 /* Should be a power of 2 */ + +/* + * Copy the initrd to /dev/ram0, copy from the end to the beginning + * to avoid taking 2x the memory. + */ +static int rd_copy_uncompressed(int ffd, int dfd) +{ + char buffer[BUF_SIZE]; + off_t bytes; + struct stat st; + + dprintf("kinit: uncompressed initrd\n"); + + if (ffd < 0 || fstat(ffd, &st) || !S_ISREG(st.st_mode) || + (bytes = st.st_size) == 0) + return -1; + + while (bytes) { + ssize_t blocksize = ((bytes - 1) & (BUF_SIZE - 1)) + 1; + off_t offset = bytes - blocksize; + + dprintf("kinit: copying %zd bytes at offset %llu\n", + blocksize, offset); + + if (xpread(ffd, buffer, blocksize, offset) != blocksize || + xpwrite(dfd, buffer, blocksize, offset) != blocksize) + return -1; + + ftruncate(ffd, offset); /* Free up memory */ + bytes = offset; + } + return 0; +} + +static int rd_copy_image(const char *path) +{ + int ffd = open(path, O_RDONLY); + int rv = -1; + unsigned char gzip_magic[2]; + + if (ffd < 0) + goto barf; + + if (xpread(ffd, gzip_magic, 2, 0) == 2 && + gzip_magic[0] == 037 && gzip_magic[1] == 0213) { + FILE *wfd = fopen("/dev/ram0", "w"); + if (!wfd) + goto barf; + rv = load_ramdisk_compressed(path, wfd, 0); + fclose(wfd); + } else { + int dfd = open("/dev/ram0", O_WRONLY); + if (dfd < 0) + goto barf; + rv = rd_copy_uncompressed(ffd, dfd); + close(dfd); + } + +barf: + if (ffd >= 0) + close(ffd); + return rv; +} + +/* + * Run /linuxrc, for emulation of old-style initrd + */ +static int run_linuxrc(int argc, char *argv[], dev_t root_dev) +{ + int root_fd, old_fd; + pid_t pid; + long realroot = Root_RAM0; + const char *ramdisk_name = "/dev/ram0"; + FILE *fp; + + dprintf("kinit: mounting initrd\n"); + mkdir("/root", 0700); + if (!mount_block(ramdisk_name, "/root", NULL, MS_VERBOSE, NULL)) + return -errno; + + /* Write the current "real root device" out to procfs */ + dprintf("kinit: real_root_dev = %#x\n", root_dev); + fp = fopen("/proc/sys/kernel/real-root-dev", "w"); + fprintf(fp, "%u", root_dev); + fclose(fp); + + mkdir("/old", 0700); + root_fd = open("/", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0); + old_fd = open("/old", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0); + + if (root_fd < 0 || old_fd < 0) + return -errno; + + if (chdir("/root") || + mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".")) + return -errno; + + pid = vfork(); + if (pid == 0) { + setsid(); + /* Looks like linuxrc doesn't get the init environment + or parameters. Weird, but so is the whole linuxrc bit. */ + execl("/linuxrc", "linuxrc", NULL); + _exit(255); + } else if (pid > 0) { + dprintf("kinit: Waiting for linuxrc to complete...\n"); + while (waitpid(pid, NULL, 0) != pid) + ; + dprintf("kinit: linuxrc done\n"); + } else { + return -errno; + } + + if (fchdir(old_fd) || + mount("/", ".", NULL, MS_MOVE, NULL) || + fchdir(root_fd) || chroot(".")) + return -errno; + + close(root_fd); + close(old_fd); + + getintfile("/proc/sys/kernel/real-root-dev", &realroot); + + /* If realroot is Root_RAM0, then the initrd did any necessary work */ + if (realroot == Root_RAM0) { + if (mount("/old", "/root", NULL, MS_MOVE, NULL)) + return -errno; + } else { + mount_root(argc, argv, (dev_t) realroot, NULL); + + /* If /root/initrd exists, move the initrd there, otherwise discard */ + if (!mount("/old", "/root/initrd", NULL, MS_MOVE, NULL)) { + /* We're good */ + } else { + int olddev = open(ramdisk_name, O_RDWR); + umount2("/old", MNT_DETACH); + if (olddev < 0 || + ioctl(olddev, BLKFLSBUF, 0) || + close(olddev)) { + fprintf(stderr, + "%s: Cannot flush initrd contents\n", + progname); + } + } + } + + rmdir("/old"); + return 0; +} + +int initrd_load(int argc, char *argv[], dev_t root_dev) +{ + if (access("/initrd.image", R_OK)) + return 0; /* No initrd */ + + dprintf("kinit: initrd found\n"); + + create_dev("/dev/ram0", Root_RAM0); + + if (rd_copy_image("/initrd.image") || unlink("/initrd.image")) { + fprintf(stderr, "%s: initrd installation failed (too big?)\n", + progname); + return 0; /* Failed to copy initrd */ + } + + dprintf("kinit: initrd copied\n"); + + if (root_dev == Root_MULTI) { + dprintf("kinit: skipping linuxrc: incompatible with multiple roots\n"); + /* Mounting initrd as ordinary root */ + return 0; + } + + if (root_dev != Root_RAM0) { + int err; + dprintf("kinit: running linuxrc\n"); + err = run_linuxrc(argc, argv, root_dev); + if (err) + fprintf(stderr, "%s: running linuxrc: %s\n", progname, + strerror(-err)); + return 1; /* initrd is root, or run_linuxrc took care of it */ + } else { + dprintf("kinit: permament (or pivoting) initrd, not running linuxrc\n"); + return 0; /* Mounting initrd as ordinary root */ + } +} diff --git a/usr/kinit/ipconfig/Kbuild b/usr/kinit/ipconfig/Kbuild new file mode 100644 index 0000000..686b03b --- /dev/null +++ b/usr/kinit/ipconfig/Kbuild @@ -0,0 +1,35 @@ +# +# Kbuild file for ipconfig +# + +static-y := static/ipconfig +shared-y := shared/ipconfig + +# common .o files +objs := main.o netdev.o packet.o +# dhcp +objs += dhcp_proto.o +# bootp +objs += bootp_proto.o + + +# TODO - do we want a stripped version +# TODO - do we want the static.g + shared.g directories? + + +# Create built-in.o with all object files (used by kinit) +lib-y := $(objs) + +# .o files used to built executables +static/ipconfig-y := $(objs) +shared/ipconfig-y := $(objs) + +# Cleaning +clean-dirs := static shared + +# install binary +ifdef KLIBCSHAREDFLAGS +install-y := $(shared-y) +else +install-y := $(static-y) +endif diff --git a/usr/kinit/ipconfig/README.ipconfig b/usr/kinit/ipconfig/README.ipconfig new file mode 100644 index 0000000..5ee87e5 --- /dev/null +++ b/usr/kinit/ipconfig/README.ipconfig @@ -0,0 +1,120 @@ +BOOTP/DHCP client for klibc +--------------------------- + +Usage: + +ipconfig [-c proto] [-d interface] [-i identifier] + [-n] [-p port] [-t timeout] [interface ...] + +-c proto Use PROTO as the configuration protocol for all + interfaces, unless overridden by specific interfaces. +-d interface Either the name of an interface, or a long spec. +-i identifier DHCP vendor class identifier. The default is + "Linux ipconfig". +-n Do nothing - just print the configuration that would + be performed. +-p port Send bootp/dhcp broadcasts from PORT, to PORT - 1. +-t timeout Give up on all unconfigured interfaces after TIMEOUT secs. + +You can configure multiple interfaces by passing multiple interface +specs on the command line, or by using the special interface name +"all". If you're autoconfiguring any interfaces, ipconfig will wait +until either all such interfaces have been configured, or the timeout +passes. + +PROTO can be one of the following, which selects the autoconfiguration +protocol to use: + +not specified use all protocols (the default) +dhcp use bootp and dhcp +bootp use bootp only +rarp use rarp (not currently supported) +none no autoconfiguration - either static config, or none at all + +An interface spec can be either short form, which is just the name of +an interface (eth0 or whatever), or long form. The long form consists +of two or more fields, separated by colons: + +<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>: + <dns0-ip>:<dns1-ip>:<ntp0-ip>:... + + <client-ip> IP address of the client. If empty, the address will + either be determined by RARP/BOOTP/DHCP. What protocol + is used de- pends on the <autoconf> parameter. If this + parameter is not empty, autoconf will be used. + + <server-ip> IP address of the NFS server. If RARP is used to + determine the client address and this parameter is NOT + empty only replies from the specified server are + accepted. To use different RARP and NFS server, + specify your RARP server here (or leave it blank), and + specify your NFS server in the `nfsroot' parameter + (see above). If this entry is blank the address of the + server is used which answered the RARP/BOOTP/DHCP + request. + + <gw-ip> IP address of a gateway if the server is on a different + subnet. If this entry is empty no gateway is used and the + server is assumed to be on the local network, unless a + value has been received by BOOTP/DHCP. + + <netmask> Netmask for local network interface. If this is empty, + the netmask is derived from the client IP address assuming + classful addressing, unless overridden in BOOTP/DHCP reply. + + <hostname> Name of the client. If empty, the client IP address is + used in ASCII notation, or the value received by + BOOTP/DHCP. + + <device> Name of network device to use. If this is empty, all + devices are used for RARP/BOOTP/DHCP requests, and the + first one we receive a reply on is configured. If you + have only one device, you can safely leave this blank. + + <autoconf> Method to use for autoconfiguration. If this is either + 'rarp', 'bootp', or 'dhcp' the specified protocol is + used. If the value is 'both', 'all' or empty, all + protocols are used. 'off', 'static' or 'none' means + no autoconfiguration. + + <dns0-ip> IP address of primary nameserver. + + Default: None if not using autoconfiguration; determined + automatically if using autoconfiguration. + + <dns1-ip> IP address of secondary nameserver. + See <dns0-ip>. + + <ntp0-ip> IP address of a Network Time Protocol (NTP) server. + Currently ignored. + + ... Additional fields will be ignored. + +IP addresses and netmasks must be either absent (defaulting to zero) +or presented in dotted-quad notation. + +An interface spec can be prefixed with either "ip=", "nfsaddrs=", both +of which are ignored. These (along with the ugliness of the long +form) are present for compatibility with the in-kernel ipconfig code +from 2.4 and earlier kernels. + +Here are a few examples of valid ipconfig command lines. + +Enable the loopback interface: + ipconfig 127.0.0.1:::::lo:none + +Try to configure eth0 using bootp for up to 30 seconds: + ipconfig -t 30 -c bootp eth0 + +Configure eth0 and eth1 using dhcp or bootp, and eth2 statically: + ipconfig -c any eth0 eth1 192.168.1.1:::::eth2:none + +-- + +From Russell's original README, and still true: + +The code in main.c is yucky imho. Needs cleaning. + +-- +Russell King (2002/10/22) +Bryan O'Sullivan (2003/04/29) diff --git a/usr/kinit/ipconfig/bootp_packet.h b/usr/kinit/ipconfig/bootp_packet.h new file mode 100644 index 0000000..1d5bd0d --- /dev/null +++ b/usr/kinit/ipconfig/bootp_packet.h @@ -0,0 +1,44 @@ +#ifndef BOOTP_PACKET_H +#define BOOTP_PACKET_H + +#include <sys/uio.h> + +struct netdev; + +/* packet ops */ +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + +/* your basic bootp packet */ +struct bootp_hdr { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + uint8_t chaddr[16]; + char server_name[64]; + char boot_file[128]; + /* 312 bytes of extensions */ +}; + +/* + * memory size of BOOTP Vendor Extensions/DHCP Options for receiving + * + * generic_ether_mtu:1500, min_sizeof(ip_hdr):20, sizeof(udp_hdr):8 + * + * #define BOOTP_EXTS_SIZE (1500 - 20 - 8 - sizeof(struct bootp_hdr)) + */ +/* larger size for backward compatibility of ipconfig */ +#define BOOTP_EXTS_SIZE 1500 + +/* minimum length of BOOTP/DHCP packet on sending */ +#define BOOTP_MIN_LEN 300 + +#endif /* BOOTP_PACKET_H */ diff --git a/usr/kinit/ipconfig/bootp_proto.c b/usr/kinit/ipconfig/bootp_proto.c new file mode 100644 index 0000000..f6f9dd4 --- /dev/null +++ b/usr/kinit/ipconfig/bootp_proto.c @@ -0,0 +1,565 @@ +/* + * BOOTP packet protocol handling. + */ +#include <sys/types.h> +#include <sys/uio.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <netinet/in.h> + +#include "ipconfig.h" +#include "netdev.h" +#include "bootp_packet.h" +#include "bootp_proto.h" +#include "packet.h" + +static uint8_t bootp_options[312] = { + [ 0] = 99, 130, 83, 99,/* RFC1048 magic cookie */ + [ 4] = 1, 4, /* 4- 9 subnet mask */ + [ 10] = 3, 4, /* 10- 15 default gateway */ + [ 16] = 5, 8, /* 16- 25 nameserver */ + [ 26] = 12, 32, /* 26- 59 host name */ + [ 60] = 40, 32, /* 60- 95 nis domain name */ + [ 96] = 17, 40, /* 96-137 boot path */ + [138] = 57, 2, 1, 150, /* 138-141 extension buffer */ + [142] = 255, /* end of list */ +}; + +/* + * Send a plain bootp request packet with options + */ +int bootp_send_request(struct netdev *dev) +{ + struct bootp_hdr bootp; + struct iovec iov[] = { + /* [0] = ip + udp headers */ + [1] = {&bootp, sizeof(bootp)}, + [2] = {bootp_options, 312} + }; + + memset(&bootp, 0, sizeof(struct bootp_hdr)); + + bootp.op = BOOTP_REQUEST, bootp.htype = dev->hwtype; + bootp.hlen = dev->hwlen; + bootp.xid = dev->bootp.xid; + bootp.ciaddr = dev->ip_addr; + bootp.secs = htons(time(NULL) - dev->open_time); + memcpy(bootp.chaddr, dev->hwaddr, 16); + + dprintf("-> bootp xid 0x%08x secs 0x%08x ", + bootp.xid, ntohs(bootp.secs)); + + return packet_send(dev, iov, 2); +} + +/* + * DESCRIPTION + * bootp_ext119_decode() decodes Domain Search Option data. + * The decoded string is separated with ' '. + * For example, it is either "foo.bar.baz. bar.baz.", "foo.bar.", or "foo.". + * + * ARGUMENTS + * const uint8_t *ext + * *ext is a pointer to a DHCP Domain Search Option data. *ext does not + * include a tag(code) octet and a length octet in DHCP options. + * For example, if *ext is {3, 'f', 'o', 'o', 0}, this function returns + * a pointer to a "foo." string. + * + * int16_t ext_size + * ext_size is the memory size of *ext. For example, + * if *ext is {3, 'f', 'o', 'o', 0}, ext_size must be 5. + * + * uint8_t *tmp + * *tmp is a pointer to a temporary memory space for decoding. + * The memory size must be equal to or more than ext_size. + * 'memset(tmp, 0, sizeof(tmp));' is not required, but values in *tmp + * are changed in decoding process. + * + * RETURN VALUE + * if OK, a pointer to a decoded string malloc-ed + * else , NULL + * + * SEE ALSO RFC3397 + */ +static char *bootp_ext119_decode(const void *ext, int16_t ext_size, void *tmp) +{ + uint8_t *u8ext; + int_fast32_t i; + int_fast32_t decoded_size; + int_fast8_t currentdomain_is_singledot; + + /* only for validating *ext */ + uint8_t *is_pointee; + int_fast32_t is_pointee_size; + + /* only for structing a decoded string */ + char *decoded_str; + int_fast32_t dst_i; + + if (ext == NULL || ext_size <= 0 || tmp == NULL) + return NULL; + + u8ext = (uint8_t *)ext; + is_pointee = tmp; + memset(is_pointee, 0, (size_t)ext_size); + is_pointee_size = 0; + + /* + * validate the format of *ext and + * calculate the memory size for a decoded string + */ + i = 0; + decoded_size = 0; + currentdomain_is_singledot = 1; + while (1) { + if (i >= ext_size) + return NULL; + + if (u8ext[i] == 0) { + /* Zero-ending */ + if (currentdomain_is_singledot) + decoded_size++; /* for '.' */ + decoded_size++; /* for ' ' or '\0' */ + currentdomain_is_singledot = 1; + i++; + if (i == ext_size) + break; + is_pointee_size = i; + } else if (u8ext[i] < 0x40) { + /* Label(sub-domain string) */ + int j; + + /* loosely validate characters for domain names */ + if (i + u8ext[i] >= ext_size) + return NULL; + for (j = i + 1; j <= i + u8ext[i]; j++) + if (!(u8ext[j] == '-' || + ('0' <= u8ext[j] && u8ext[j] <= '9') || + ('A' <= u8ext[j] && u8ext[j] <= 'Z') || + ('a' <= u8ext[j] && u8ext[j] <= 'z'))) + return NULL; + + is_pointee[i] = 1; + decoded_size += u8ext[i] + 1; /* for Label + '.' */ + currentdomain_is_singledot = 0; + i += u8ext[i] + 1; + } else if (u8ext[i] < 0xc0) + return NULL; + + else { + /* Compression-pointer (to a prior Label) */ + int_fast32_t p; + + if (i + 1 >= ext_size) + return NULL; + + p = ((0x3f & u8ext[i]) << 8) + u8ext[i + 1]; + if (!(p < is_pointee_size && is_pointee[p])) + return NULL; + + while (1) { + /* u8ext[p] was validated */ + if (u8ext[p] == 0) { + /* Zero-ending */ + decoded_size++; + break; + } else if (u8ext[p] < 0x40) { + /* Label(sub-domain string) */ + decoded_size += u8ext[p] + 1; + p += u8ext[p] + 1; + } else { + /* Compression-pointer */ + p = ((0x3f & u8ext[p]) << 8) + + u8ext[p + 1]; + } + } + + currentdomain_is_singledot = 1; + i += 2; + if (i == ext_size) + break; + is_pointee_size = i; + } + } + + + /* + * construct a decoded string + */ + decoded_str = malloc(decoded_size); + if (decoded_str == NULL) + return NULL; + + i = 0; + dst_i = 0; + currentdomain_is_singledot = 1; + while (1) { + if (u8ext[i] == 0) { + /* Zero-ending */ + if (currentdomain_is_singledot) { + if (dst_i != 0) + dst_i++; + decoded_str[dst_i] = '.'; + } + dst_i++; + decoded_str[dst_i] = ' '; + + currentdomain_is_singledot = 1; + i++; + if (i == ext_size) + break; + } else if (u8ext[i] < 0x40) { + /* Label(sub-domain string) */ + if (dst_i != 0) + dst_i++; + memcpy(&decoded_str[dst_i], &u8ext[i + 1], + (size_t)u8ext[i]); + dst_i += u8ext[i]; + decoded_str[dst_i] = '.'; + + currentdomain_is_singledot = 0; + i += u8ext[i] + 1; + } else { + /* Compression-pointer (to a prior Label) */ + int_fast32_t p; + + p = ((0x3f & u8ext[i]) << 8) + u8ext[i + 1]; + while (1) { + if (u8ext[p] == 0) { + /* Zero-ending */ + decoded_str[dst_i++] = '.'; + decoded_str[dst_i] = ' '; + break; + } else if (u8ext[p] < 0x40) { + /* Label(sub-domain string) */ + dst_i++; + memcpy(&decoded_str[dst_i], + &u8ext[p + 1], + (size_t)u8ext[p]); + dst_i += u8ext[p]; + decoded_str[dst_i] = '.'; + + p += u8ext[p] + 1; + } else { + /* Compression-pointer */ + p = ((0x3f & u8ext[p]) << 8) + + u8ext[p + 1]; + } + } + + currentdomain_is_singledot = 1; + i += 2; + if (i == ext_size) + break; + } + } + decoded_str[dst_i] = '\0'; +#ifdef DEBUG + if (dst_i + 1 != decoded_size) { + dprintf("bug:%s():bottom: malloc(%ld), write(%ld)\n", + __func__, (long)decoded_size, (long)(dst_i + 1)); + exit(1); + } +#endif + return decoded_str; +} + +/* + * DESCRIPTION + * bootp_ext121_decode() decodes Classless Route Option data. + * + * ARGUMENTS + * const uint8_t *ext + * *ext is a pointer to a DHCP Classless Route Option data. + * For example, if *ext is {16, 192, 168, 192, 168, 42, 1}, + * this function returns a pointer to + * { + * subnet = 192.168.0.0; + * netmask_width = 16; + * gateway = 192.168.42.1; + * next = NULL; + * } + * + * int16_t ext_size + * ext_size is the memory size of *ext. For example, + * if *ext is {16, 192, 168, 192, 168, 42, 1}, ext_size must be 7. + * + * RETURN VALUE + * if OK, a pointer to a decoded struct route malloc-ed + * else , NULL + * + * SEE ALSO RFC3442 + */ +struct route *bootp_ext121_decode(const uint8_t *ext, int16_t ext_size) +{ + int16_t index = 0; + uint8_t netmask_width; + uint8_t significant_octets; + struct route *routes = NULL; + struct route *prev_route = NULL; + + while (index < ext_size) { + netmask_width = ext[index]; + index++; + if (netmask_width > 32) { + printf("IP-Config: Given Classless Route Option subnet mask width '%u' " + "exceeds IPv4 limit of 32. Ignoring remaining option.\n", + netmask_width); + return routes; + } + significant_octets = netmask_width / 8 + (netmask_width % 8 > 0); + if (ext_size - index < significant_octets + 4) { + printf("IP-Config: Given Classless Route Option remaining lengths (%u octets) " + "is shorter than the expected %u octets. Ignoring remaining options.\n", + ext_size - index, significant_octets + 4); + return routes; + } + + struct route *route = malloc(sizeof(struct route)); + if (route == NULL) + return routes; + + /* convert only significant octets from byte array into integer in network byte order */ + route->subnet = 0; + memcpy(&route->subnet, &ext[index], significant_octets); + index += significant_octets; + /* RFC3442 demands: After deriving a subnet number and subnet mask from + each destination descriptor, the DHCP client MUST zero any bits in + the subnet number where the corresponding bit in the mask is zero. */ + route->subnet &= netdev_genmask(netmask_width); + + /* convert octet array into network byte order */ + memcpy(&route->gateway, &ext[index], 4); + index += 4; + + route->netmask_width = netmask_width; + route->next = NULL; + + if (prev_route == NULL) { + routes = route; + } else { + prev_route->next = route; + } + prev_route = route; + } + return routes; +} + +/* + * Parse a bootp reply packet + */ +int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr, + uint8_t *exts, int extlen) +{ + uint8_t ext119_buf[BOOTP_EXTS_SIZE]; + int16_t ext119_len = 0; + uint8_t ext121_buf[BOOTP_EXTS_SIZE]; + int16_t ext121_len = 0; + + dev->bootp.gateway = hdr->giaddr; + dev->ip_addr = hdr->yiaddr; + dev->ip_server = hdr->siaddr; + dev->ip_netmask = INADDR_ANY; + dev->ip_broadcast = INADDR_ANY; + dev->ip_gateway = hdr->giaddr; + dev->ip_nameserver[0] = INADDR_ANY; + dev->ip_nameserver[1] = INADDR_ANY; + dev->hostname[0] = '\0'; + dev->nisdomainname[0] = '\0'; + dev->bootpath[0] = '\0'; + memcpy(&dev->filename, &hdr->boot_file, FNLEN); + + if (extlen >= 4 && exts[0] == 99 && exts[1] == 130 && + exts[2] == 83 && exts[3] == 99) { + uint8_t *ext; + + for (ext = exts + 4; ext - exts < extlen;) { + int len; + uint8_t opt = *ext++; + + if (opt == 0) + continue; + else if (opt == 255) + break; + + if (ext - exts >= extlen) + break; + len = *ext++; + + if (ext - exts + len > extlen) + break; + switch (opt) { + case 1: /* subnet mask */ + if (len == 4) + memcpy(&dev->ip_netmask, ext, 4); + break; + case 3: /* default gateway */ + if (len >= 4) + memcpy(&dev->ip_gateway, ext, 4); + break; + case 6: /* DNS server */ + if (len >= 4) + memcpy(&dev->ip_nameserver, ext, + len >= 8 ? 8 : 4); + break; + case 12: /* host name */ + if (len > sizeof(dev->hostname) - 1) + len = sizeof(dev->hostname) - 1; + memcpy(&dev->hostname, ext, len); + dev->hostname[len] = '\0'; + break; + case 15: /* domain name */ + if (len > sizeof(dev->dnsdomainname) - 1) + len = sizeof(dev->dnsdomainname) - 1; + memcpy(&dev->dnsdomainname, ext, len); + dev->dnsdomainname[len] = '\0'; + break; + case 17: /* root path */ + if (len > sizeof(dev->bootpath) - 1) + len = sizeof(dev->bootpath) - 1; + memcpy(&dev->bootpath, ext, len); + dev->bootpath[len] = '\0'; + break; + case 26: /* interface MTU */ + if (len == 2) + dev->mtu = (ext[0] << 8) + ext[1]; + break; + case 28: /* broadcast addr */ + if (len == 4) + memcpy(&dev->ip_broadcast, ext, 4); + break; + case 40: /* NIS domain name */ + if (len > sizeof(dev->nisdomainname) - 1) + len = sizeof(dev->nisdomainname) - 1; + memcpy(&dev->nisdomainname, ext, len); + dev->nisdomainname[len] = '\0'; + break; + case 54: /* server identifier */ + if (len == 4 && !dev->ip_server) + memcpy(&dev->ip_server, ext, 4); + break; + case 119: /* Domain Search Option */ + if (ext119_len >= 0 && + ext119_len + len <= sizeof(ext119_buf)) { + memcpy(ext119_buf + ext119_len, + ext, len); + ext119_len += len; + } else + ext119_len = -1; + + break; + case 121: /* Classless Static Route Option (RFC3442) */ + if (ext121_len >= 0 && + ext121_len + len <= sizeof(ext121_buf)) { + memcpy(ext121_buf + ext121_len, + ext, len); + ext121_len += len; + } else + ext121_len = -1; + + break; + } + + ext += len; + } + } + if (ext119_len > 0) { + char *ret; + uint8_t ext119_tmp[BOOTP_EXTS_SIZE]; + + ret = bootp_ext119_decode(ext119_buf, ext119_len, ext119_tmp); + if (ret != NULL) { + if (dev->domainsearch != NULL) + free(dev->domainsearch); + dev->domainsearch = ret; + } + } + + if (ext121_len > 0) { + struct route *ret; + + ret = bootp_ext121_decode(ext121_buf, ext121_len); + if (ret != NULL) { + struct route *cur = dev->routes; + struct route *next; + while (cur != NULL) { + next = cur->next; + free(cur); + cur = next; + } + dev->routes = ret; + } + } + + /* + * Got packet. + */ + return 1; +} + +/* + * Receive a bootp reply and parse packet + * Returns: + *-1 = Error in packet_recv, try again later + * 0 = Unexpected packet, discarded + * 1 = Correctly received and parsed packet + */ +int bootp_recv_reply(struct netdev *dev) +{ + struct bootp_hdr bootp; + uint8_t bootp_options[BOOTP_EXTS_SIZE]; + struct iovec iov[] = { + /* [0] = ip + udp headers */ + [1] = {&bootp, sizeof(struct bootp_hdr)}, + [2] = {bootp_options, sizeof(bootp_options)} + }; + int ret; + + ret = packet_recv(dev, iov, 3); + if (ret <= 0) + return ret; + + if (ret < sizeof(struct bootp_hdr) || + bootp.op != BOOTP_REPLY || /* RFC951 7.5 */ + bootp.xid != dev->bootp.xid || + memcmp(bootp.chaddr, dev->hwaddr, 16)) + return 0; + + ret -= sizeof(struct bootp_hdr); + + return bootp_parse(dev, &bootp, bootp_options, ret); +} + +/* + * Initialise interface for bootp. + */ +int bootp_init_if(struct netdev *dev) +{ + short flags; + + /* + * Get the device flags + */ + if (netdev_getflags(dev, &flags)) + return -1; + + /* + * We can't do DHCP nor BOOTP if this device + * doesn't support broadcast. + */ + if (dev->mtu < 364 || (flags & IFF_BROADCAST) == 0) { + dev->caps &= ~(CAP_BOOTP | CAP_DHCP); + return 0; + } + + /* + * Get a random XID + */ + dev->bootp.xid = (uint32_t) lrand48(); + dev->open_time = time(NULL); + + return 0; +} diff --git a/usr/kinit/ipconfig/bootp_proto.h b/usr/kinit/ipconfig/bootp_proto.h new file mode 100644 index 0000000..60873ce --- /dev/null +++ b/usr/kinit/ipconfig/bootp_proto.h @@ -0,0 +1,10 @@ +#ifndef IPCONFIG_BOOTP_PROTO_H +#define IPCONFIG_BOOTP_PROTO_H + +int bootp_send_request(struct netdev *dev); +int bootp_recv_reply(struct netdev *dev); +int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr, uint8_t * exts, + int extlen); +int bootp_init_if(struct netdev *dev); + +#endif /* IPCONFIG_BOOTP_PROTO_H */ diff --git a/usr/kinit/ipconfig/dhcp_proto.c b/usr/kinit/ipconfig/dhcp_proto.c new file mode 100644 index 0000000..4e560b8 --- /dev/null +++ b/usr/kinit/ipconfig/dhcp_proto.c @@ -0,0 +1,301 @@ +/* + * DHCP RFC 2131 and 2132 + */ +#include <sys/types.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "ipconfig.h" +#include "netdev.h" +#include "bootp_packet.h" +#include "bootp_proto.h" +#include "dhcp_proto.h" +#include "packet.h" + +static uint8_t dhcp_params[] = { + 1, /* subnet mask */ + 3, /* default gateway */ + 6, /* DNS server */ + 12, /* host name */ + 15, /* domain name */ + 17, /* root path */ + 26, /* interface mtu */ + 28, /* broadcast addr */ + 40, /* NIS domain name (why?) */ + 119, /* Domain Search Option */ + 121, /* Classless Static Route Option (RFC3442) */ +}; + +static uint8_t dhcp_discover_hdr[] = { + 99, 130, 83, 99, /* bootp cookie */ + 53, 1, DHCPDISCOVER, /* dhcp message type */ + 55, sizeof(dhcp_params), /* parameter list */ +}; + +static uint8_t dhcp_request_hdr[] = { + 99, 130, 83, 99, /* boot cookie */ + 53, 1, DHCPREQUEST, /* dhcp message type */ +#define SERVER_IP_OFF 9 + 54, 4, 0, 0, 0, 0, /* server IP */ +#define REQ_IP_OFF 15 + 50, 4, 0, 0, 0, 0, /* requested IP address */ + 55, sizeof(dhcp_params), /* parameter list */ +}; + +static uint8_t dhcp_end[] = { + 255, +}; + +/* Both iovecs below have to have the same structure, since dhcp_send() + pokes at the internals */ +#define DHCP_IOV_LEN 8 + +static struct iovec dhcp_discover_iov[DHCP_IOV_LEN] = { + /* [0] = ip + udp header */ + /* [1] = bootp header */ + [2] = {dhcp_discover_hdr, sizeof(dhcp_discover_hdr)}, + [3] = {dhcp_params, sizeof(dhcp_params)}, + /* [4] = optional vendor class */ + /* [5] = optional hostname */ + /* [6] = {dhcp_end, sizeof(dhcp_end)} */ + /* [7] = optional padding */ +}; + +static struct iovec dhcp_request_iov[DHCP_IOV_LEN] = { + /* [0] = ip + udp header */ + /* [1] = bootp header */ + [2] = {dhcp_request_hdr, sizeof(dhcp_request_hdr)}, + [3] = {dhcp_params, sizeof(dhcp_params)}, + /* [4] = optional vendor class */ + /* [5] = optional hostname */ + /* [6] = {dhcp_end, sizeof(dhcp_end)} */ + /* [7] = optional padding */ +}; + +/* + * Parse a DHCP response packet + * Returns: + * 0 = Unexpected packet, not parsed + * 2 = DHCPOFFER (from dhcp_proto.h) + * 5 = DHCPACK + * 6 = DHCPNACK + */ +static int dhcp_parse(struct netdev *dev, struct bootp_hdr *hdr, + uint8_t *exts, int extlen) +{ + uint8_t type = 0; + uint32_t serverid = INADDR_NONE; + uint32_t leasetime = 0; + int ret = 0; + + if (extlen >= 4 && exts[0] == 99 && exts[1] == 130 && + exts[2] == 83 && exts[3] == 99) { + uint8_t *ext; + + for (ext = exts + 4; ext - exts < extlen;) { + int len; + uint8_t opt = *ext++; + + if (opt == 0) + continue; + else if (opt == 255) + break; + + if (ext - exts >= extlen) + break; + len = *ext++; + + if (ext - exts + len > extlen) + break; + switch (opt) { + case 51: /* IP Address Lease Time */ + if (len == 4) + leasetime = ntohl(*(uint32_t *)ext); + break; + case 53: /* DHCP Message Type */ + if (len == 1) + type = *ext; + break; + case 54: /* Server Identifier */ + if (len == 4) + memcpy(&serverid, ext, 4); + break; + } + ext += len; + } + } + + switch (type) { + case DHCPOFFER: + ret = bootp_parse(dev, hdr, exts, extlen) ? DHCPOFFER : 0; + if (ret == DHCPOFFER && serverid != INADDR_NONE) + dev->serverid = serverid; + dprintf("\n dhcp offer\n"); + break; + + case DHCPACK: + dev->dhcpleasetime = leasetime; + ret = bootp_parse(dev, hdr, exts, extlen) ? DHCPACK : 0; + dprintf("\n dhcp ack\n"); + break; + + case DHCPNAK: + ret = DHCPNAK; + dprintf("\n dhcp nak\n"); + break; + } + return ret; +} + +/* + * Receive and parse a DHCP packet + * Returns: + *-1 = Error in packet_recv, try again later + * 0 = Unexpected packet, discarded + * 2 = DHCPOFFER (from dhcp_proto.h) + * 5 = DHCPACK + * 6 = DHCPNACK + */ +static int dhcp_recv(struct netdev *dev) +{ + struct bootp_hdr bootp; + uint8_t dhcp_options[BOOTP_EXTS_SIZE]; + struct iovec iov[] = { + /* [0] = ip + udp header */ + [1] = {&bootp, sizeof(struct bootp_hdr)}, + [2] = {dhcp_options, sizeof(dhcp_options)} + }; + int ret; + + ret = packet_recv(dev, iov, 3); + if (ret <= 0) + return ret; + + dprintf("\n dhcp xid %08x ", dev->bootp.xid); + + if (ret < sizeof(struct bootp_hdr) || bootp.op != BOOTP_REPLY || + /* RFC951 7.5 */ bootp.xid != dev->bootp.xid || + memcmp(bootp.chaddr, dev->hwaddr, 16)) + return 0; + + ret -= sizeof(struct bootp_hdr); + + return dhcp_parse(dev, &bootp, dhcp_options, ret); +} + +static int dhcp_send(struct netdev *dev, struct iovec *vec) +{ + struct bootp_hdr bootp; + char dhcp_hostname[SYS_NMLN+2]; + uint8_t padding[BOOTP_MIN_LEN - sizeof(struct bootp_hdr)]; + int padding_len; + int i = 4; + int j; + + memset(&bootp, 0, sizeof(struct bootp_hdr)); + + bootp.op = BOOTP_REQUEST; + bootp.htype = dev->hwtype; + bootp.hlen = dev->hwlen; + bootp.xid = dev->bootp.xid; + bootp.ciaddr = INADDR_ANY; + /* yiaddr should always be set to 0 for the messages we're likely + * to send as a DHCP client: DHCPDISCOVER, DHCPREQUEST, DHCPDECLINE, + * DHCPINFORM, DHCPRELEASE + * cf. RFC2131 section 4.1.1, table 5. + */ + bootp.yiaddr = INADDR_ANY; + bootp.giaddr = INADDR_ANY; + bootp.flags = htons(0x8000); + bootp.secs = htons(time(NULL) - dev->open_time); + memcpy(bootp.chaddr, dev->hwaddr, 16); + + vec[1].iov_base = &bootp; + vec[1].iov_len = sizeof(struct bootp_hdr); + + dprintf("xid %08x secs %d ", bootp.xid, ntohs(bootp.secs)); + + if (vendor_class_identifier_len > 2) { + vec[i].iov_base = vendor_class_identifier; + vec[i].iov_len = vendor_class_identifier_len; + i++; + + dprintf("vendor_class_identifier \"%.*s\" ", + vendor_class_identifier_len-2, + vendor_class_identifier+2); + } + + if (dev->reqhostname[0] != '\0') { + int len = strlen(dev->reqhostname); + dhcp_hostname[0] = 12; + dhcp_hostname[1] = len; + memcpy(dhcp_hostname+2, dev->reqhostname, len); + + vec[i].iov_base = dhcp_hostname; + vec[i].iov_len = len+2; + i++; + + printf("hostname %.*s ", len, dhcp_hostname+2); + } + + vec[i].iov_base = dhcp_end; + vec[i].iov_len = sizeof(dhcp_end); + + /* Append padding if DHCP packet length is shorter than BOOTP_MIN_LEN */ + padding_len = sizeof(padding); + for (j = 2; j <= i; j++) + padding_len -= vec[j].iov_len; + if (padding_len > 0) { + memset(padding, 0, padding_len); + i++; + vec[i].iov_base = padding; + vec[i].iov_len = padding_len; + } + + return packet_send(dev, vec, i + 1); +} + +/* + * Send a DHCP discover packet + */ +int dhcp_send_discover(struct netdev *dev) +{ + dev->ip_addr = INADDR_ANY; + dev->ip_gateway = INADDR_ANY; + + dprintf("-> dhcp discover "); + + return dhcp_send(dev, dhcp_discover_iov); +} + +/* + * Receive a DHCP offer packet + */ +int dhcp_recv_offer(struct netdev *dev) +{ + return dhcp_recv(dev); +} + +/* + * Send a DHCP request packet + */ +int dhcp_send_request(struct netdev *dev) +{ + memcpy(&dhcp_request_hdr[SERVER_IP_OFF], &dev->serverid, 4); + memcpy(&dhcp_request_hdr[REQ_IP_OFF], &dev->ip_addr, 4); + + dprintf("-> dhcp request "); + + return dhcp_send(dev, dhcp_request_iov); +} + +/* + * Receive a DHCP ack packet + */ +int dhcp_recv_ack(struct netdev *dev) +{ + return dhcp_recv(dev); +} diff --git a/usr/kinit/ipconfig/dhcp_proto.h b/usr/kinit/ipconfig/dhcp_proto.h new file mode 100644 index 0000000..0fba92f --- /dev/null +++ b/usr/kinit/ipconfig/dhcp_proto.h @@ -0,0 +1,19 @@ +#ifndef IPCONFIG_DHCP_PROTO_H +#define IPCONFIG_DHCP_PROTO_H + +/* DHCP message types */ +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +int dhcp_send_discover(struct netdev *dev); +int dhcp_recv_offer(struct netdev *dev); +int dhcp_send_request(struct netdev *dev); +int dhcp_recv_ack(struct netdev *dev); + +#endif /* IPCONFIG_DHCP_PROTO_H */ diff --git a/usr/kinit/ipconfig/ipconfig.h b/usr/kinit/ipconfig/ipconfig.h new file mode 100644 index 0000000..d1d7e42 --- /dev/null +++ b/usr/kinit/ipconfig/ipconfig.h @@ -0,0 +1,25 @@ +#ifndef IPCONFIG_IPCONFIG_H +#define IPCONFIG_IPCONFIG_H + +#include <stdint.h> +#include <sys/types.h> + +#define LOCAL_PORT 68 +#define REMOTE_PORT (LOCAL_PORT - 1) + +extern uint16_t cfg_local_port; +extern uint16_t cfg_remote_port; + +extern char vendor_class_identifier[]; +extern int vendor_class_identifier_len; + +int ipconfig_main(int argc, char *argv[]); +uint32_t ipconfig_server_address(void *next); + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +#endif /* IPCONFIG_IPCONFIG_H */ diff --git a/usr/kinit/ipconfig/main.c b/usr/kinit/ipconfig/main.c new file mode 100644 index 0000000..64c5398 --- /dev/null +++ b/usr/kinit/ipconfig/main.c @@ -0,0 +1,924 @@ +#include <poll.h> +#include <limits.h> +#include <setjmp.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/sysinfo.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> /* for getopts */ + +#include <net/if_arp.h> + +#include "ipconfig.h" +#include "netdev.h" +#include "bootp_packet.h" +#include "bootp_proto.h" +#include "dhcp_proto.h" +#include "packet.h" + +static const char sysfs_class_net[] = "/sys/class/net"; +static const char *progname; +static jmp_buf abort_buf; +static char do_not_config; +static unsigned int default_caps = CAP_DHCP | CAP_BOOTP | CAP_RARP; +static int loop_timeout = -1; +static int configured; +static int bringup_first = 0; +static int n_devices = 0; + +/* DHCP vendor class identifier */ +char vendor_class_identifier[260]; +int vendor_class_identifier_len; + +struct state { + int state; + int restart_state; + time_t expire; + int retry_period; + + struct netdev *dev; + struct state *next; +}; + +/* #define PROTO_x : for uint8_t proto of struct netdev */ +struct protoinfo { + char *name; +} protoinfos[] = { +#define PROTO_NONE 0 + {"none"}, +#define PROTO_BOOTP 1 + {"bootp"}, +#define PROTO_DHCP 2 + {"dhcp"}, +#define PROTO_RARP 3 + {"rarp"} +}; + +static inline const char *my_inet_ntoa(uint32_t addr) +{ + struct in_addr a; + + a.s_addr = addr; + + return inet_ntoa(a); +} + +static void print_device_config(struct netdev *dev) +{ + int dns0_spaces; + int dns1_spaces; + printf("IP-Config: %s complete", dev->name); + if (dev->proto == PROTO_BOOTP || dev->proto == PROTO_DHCP) + printf(" (%s from %s)", protoinfos[dev->proto].name, + my_inet_ntoa(dev->serverid ? + dev->serverid : dev->ip_server)); + + printf(":\n address: %-16s ", my_inet_ntoa(dev->ip_addr)); + printf("broadcast: %-16s ", my_inet_ntoa(dev->ip_broadcast)); + printf("netmask: %-16s\n", my_inet_ntoa(dev->ip_netmask)); + if (dev->routes != NULL) { + struct route *cur; + char *delim = ""; + printf(" routes :"); + for (cur = dev->routes; cur != NULL; cur = cur->next) { + printf("%s %s/%u", delim, my_inet_ntoa(cur->subnet), cur->netmask_width); + if (cur->gateway != 0) { + printf(" via %s", my_inet_ntoa(cur->gateway)); + } + delim = ","; + } + printf("\n"); + dns0_spaces = 3; + dns1_spaces = 5; + } else { + printf(" gateway: %-16s", my_inet_ntoa(dev->ip_gateway)); + dns0_spaces = 5; + dns1_spaces = 3; + } + printf(" dns0%*c: %-16s", dns0_spaces, ' ', my_inet_ntoa(dev->ip_nameserver[0])); + printf(" dns1%*c: %-16s\n", dns1_spaces, ' ', my_inet_ntoa(dev->ip_nameserver[1])); + if (dev->hostname[0]) + printf(" host : %-64s\n", dev->hostname); + if (dev->dnsdomainname[0]) + printf(" domain : %-64s\n", dev->dnsdomainname); + if (dev->nisdomainname[0]) + printf(" nisdomain: %-64s\n", dev->nisdomainname); + printf(" rootserver: %s ", my_inet_ntoa(dev->ip_server)); + printf("rootpath: %s\n", dev->bootpath); + printf(" filename : %s\n", dev->filename); +} + +static void configure_device(struct netdev *dev) +{ + if (do_not_config) + return; + + if (netdev_setmtu(dev)) + printf("IP-Config: failed to set MTU on %s to %u\n", + dev->name, dev->mtu); + + if (netdev_setaddress(dev)) + printf("IP-Config: failed to set addresses on %s\n", + dev->name); + if (netdev_setroutes(dev)) + printf("IP-Config: failed to set routes on %s\n", + dev->name); + if (dev->hostname[0] && + sethostname(dev->hostname, strlen(dev->hostname))) + printf("IP-Config: failed to set hostname '%s' from %s\n", + dev->hostname, dev->name); +} + +/* + * Escape shell varialbes in git style: + * Always start with a single quote ('), then leave all characters + * except ' and ! unchanged. + */ +static void write_option(FILE *f, const char *name, const char *chr) +{ + + fprintf(f, "%s='", name); + while (*chr) { + switch (*chr) { + case '!': + case '\'': + fprintf(f, "'\\%c'", *chr); + break; + default: + fprintf(f, "%c", *chr); + break; + } + ++chr; + } + fprintf(f, "'\n"); +} + +static void dump_device_config(struct netdev *dev) +{ + char fn[40]; + FILE *f; + /* + * char UINT64_MAX[] = "18446744073709551615"; + * sizeof(UINT64_MAX)==21 + */ + char buf21[21]; + const char path[] = "/run/"; + + snprintf(fn, sizeof(fn), "%snet-%s.conf", path, dev->name); + f = fopen(fn, "w"); + if (f) { + write_option(f, "DEVICE", dev->name); + write_option(f, "PROTO", protoinfos[dev->proto].name); + write_option(f, "IPV4ADDR", + my_inet_ntoa(dev->ip_addr)); + write_option(f, "IPV4BROADCAST", + my_inet_ntoa(dev->ip_broadcast)); + write_option(f, "IPV4NETMASK", + my_inet_ntoa(dev->ip_netmask)); + if (dev->routes != NULL) { + /* Use 6 digits to encode the index */ + char key[23]; + char value[19]; + int i = 0; + struct route *cur; + for (cur = dev->routes; cur != NULL; cur = cur->next) { + snprintf(key, sizeof(key), "IPV4ROUTE%iSUBNET", i); + snprintf(value, sizeof(value), "%s/%u", my_inet_ntoa(cur->subnet), cur->netmask_width); + write_option(f, key, value); + snprintf(key, sizeof(key), "IPV4ROUTE%iGATEWAY", i); + write_option(f, key, my_inet_ntoa(cur->gateway)); + i++; + } + } else { + write_option(f, "IPV4GATEWAY", + my_inet_ntoa(dev->ip_gateway)); + } + write_option(f, "IPV4DNS0", + my_inet_ntoa(dev->ip_nameserver[0])); + write_option(f, "IPV4DNS1", + my_inet_ntoa(dev->ip_nameserver[1])); + write_option(f, "HOSTNAME", dev->hostname); + write_option(f, "DNSDOMAIN", dev->dnsdomainname); + write_option(f, "NISDOMAIN", dev->nisdomainname); + write_option(f, "ROOTSERVER", + my_inet_ntoa(dev->ip_server)); + write_option(f, "ROOTPATH", dev->bootpath); + write_option(f, "filename", dev->filename); + sprintf(buf21, "%ld", (long)dev->uptime); + write_option(f, "UPTIME", buf21); + sprintf(buf21, "%u", (unsigned int)dev->dhcpleasetime); + write_option(f, "DHCPLEASETIME", buf21); + write_option(f, "DOMAINSEARCH", dev->domainsearch == NULL ? + "" : dev->domainsearch); + fclose(f); + } +} + +static uint32_t inet_class_netmask(uint32_t ip) +{ + ip = ntohl(ip); + if (IN_CLASSA(ip)) + return htonl(IN_CLASSA_NET); + if (IN_CLASSB(ip)) + return htonl(IN_CLASSB_NET); + if (IN_CLASSC(ip)) + return htonl(IN_CLASSC_NET); + return INADDR_ANY; +} + +static void postprocess_device(struct netdev *dev) +{ + if (dev->ip_netmask == INADDR_ANY) { + dev->ip_netmask = inet_class_netmask(dev->ip_addr); + printf("IP-Config: %s guessed netmask %s\n", + dev->name, my_inet_ntoa(dev->ip_netmask)); + } + if (dev->ip_broadcast == INADDR_ANY) { + dev->ip_broadcast = + (dev->ip_addr & dev->ip_netmask) | ~dev->ip_netmask; + printf("IP-Config: %s guessed broadcast address %s\n", + dev->name, my_inet_ntoa(dev->ip_broadcast)); + } +} + +static void complete_device(struct netdev *dev) +{ + struct sysinfo info; + + if (!sysinfo(&info)) + dev->uptime = info.uptime; + postprocess_device(dev); + configure_device(dev); + dump_device_config(dev); + print_device_config(dev); + packet_close(dev); + + ++configured; + + dev->next = ifaces; + ifaces = dev; +} + +/* + * Returns: + * 0 = Not handled, try again later + * 1 = Handled + */ +static int process_receive_event(struct state *s, time_t now) +{ + int handled = 1; + + switch (s->state) { + case DEVST_ERROR: + return 0; /* Not handled */ + case DEVST_COMPLETE: + return 0; /* Not handled as already configured */ + + case DEVST_BOOTP: + s->restart_state = DEVST_BOOTP; + switch (bootp_recv_reply(s->dev)) { + case -1: + s->state = DEVST_ERROR; + break; + case 0: + handled = 0; + break; + case 1: + s->state = DEVST_COMPLETE; + s->dev->proto = PROTO_BOOTP; + dprintf("\n bootp reply\n"); + break; + } + break; + + case DEVST_DHCPDISC: + s->restart_state = DEVST_DHCPDISC; + switch (dhcp_recv_offer(s->dev)) { + case -1: + s->state = DEVST_ERROR; + break; + case 0: + handled = 0; + break; + case DHCPOFFER: /* Offer received */ + s->state = DEVST_DHCPREQ; + dhcp_send_request(s->dev); + break; + } + break; + + case DEVST_DHCPREQ: + s->restart_state = DEVST_DHCPDISC; + switch (dhcp_recv_ack(s->dev)) { + case -1: /* error */ + s->state = DEVST_ERROR; + break; + case 0: + handled = 0; + break; + case DHCPACK: /* ACK received */ + s->state = DEVST_COMPLETE; + s->dev->proto = PROTO_DHCP; + break; + case DHCPNAK: /* NAK received */ + s->state = DEVST_DHCPDISC; + break; + } + break; + + default: + dprintf("\n"); + handled = 0; + break; + } + + switch (s->state) { + case DEVST_COMPLETE: + complete_device(s->dev); + break; + + case DEVST_ERROR: + /* error occurred, try again in 10 seconds */ + s->expire = now + 10; + break; + } + + return handled; +} + +static void process_timeout_event(struct state *s, time_t now) +{ + int ret = 0; + + /* + * If we had an error, restore a sane state to + * restart from. + */ + if (s->state == DEVST_ERROR) + s->state = s->restart_state; + + /* + * Now send a packet depending on our state. + */ + switch (s->state) { + case DEVST_BOOTP: + ret = bootp_send_request(s->dev); + s->restart_state = DEVST_BOOTP; + break; + + case DEVST_DHCPDISC: + ret = dhcp_send_discover(s->dev); + s->restart_state = DEVST_DHCPDISC; + break; + + case DEVST_DHCPREQ: + ret = dhcp_send_request(s->dev); + s->restart_state = DEVST_DHCPDISC; + break; + } + + if (ret == -1) { + s->state = DEVST_ERROR; + s->expire = now + 1; + } else { + s->expire = now + s->retry_period; + + s->retry_period *= 2; + if (s->retry_period > 60) + s->retry_period = 60; + } +} + +static void process_error_event(struct state *s, time_t now) +{ + s->state = DEVST_ERROR; + s->expire = now + 1; +} + +static struct state *slist; +struct netdev *ifaces; + +/* + * Returns: + * 0 = No dhcp/bootp packet was received + * 1 = A packet was received and handled + */ +static int do_pkt_recv(int nr, struct pollfd *fds, time_t now) +{ + int i, ret = 0; + struct state *s; + + for (i = 0, s = slist; s && nr; s = s->next) { + if (s->dev->pkt_fd != fds[i].fd) + continue; + if (fds[i].revents) { + if (fds[i].revents & POLLRDNORM) + ret |= process_receive_event(s, now); + else + process_error_event(s, now); + nr--; + } + i++; + } + return ret; +} + +static int loop(void) +{ + struct pollfd *fds; + struct state *s; + int i, nr = 0, rc = 0; + struct timeval now, prev; + time_t start; + + fds = malloc(sizeof(struct pollfd) * n_devices); + if (!fds) { + fprintf(stderr, "malloc failed\n"); + rc = -1; + goto bail; + } + + memset(fds, 0, sizeof(*fds)); + + gettimeofday(&now, NULL); + start = now.tv_sec; + while (1) { + int timeout = 60; + int pending = 0; + int done = 0; + int timeout_ms; + int x; + + for (i = 0, s = slist; s; s = s->next) { + dprintf("%s: state = %d\n", s->dev->name, s->state); + + if (s->state == DEVST_COMPLETE) { + done++; + continue; + } + + pending++; + + if (s->expire - now.tv_sec <= 0) { + dprintf("timeout\n"); + process_timeout_event(s, now.tv_sec); + } + + if (s->state != DEVST_ERROR) { + fds[i].fd = s->dev->pkt_fd; + fds[i].events = POLLRDNORM; + i++; + } + + if (timeout > s->expire - now.tv_sec) + timeout = s->expire - now.tv_sec; + } + + if (pending == 0 || (bringup_first && done)) + break; + + timeout_ms = timeout * 1000; + + for (x = 0; x < 2; x++) { + int delta_ms; + + if (timeout_ms <= 0) + timeout_ms = 100; + + nr = poll(fds, i, timeout_ms); + prev = now; + gettimeofday(&now, NULL); + + if ((nr > 0) && do_pkt_recv(nr, fds, now.tv_sec)) + break; + + if (loop_timeout >= 0 && + now.tv_sec - start >= loop_timeout) { + printf("IP-Config: no response after %d " + "secs - giving up\n", loop_timeout); + rc = -1; + goto bail; + } + + delta_ms = (now.tv_sec - prev.tv_sec) * 1000; + delta_ms += (now.tv_usec - prev.tv_usec) / 1000; + + dprintf("Delta: %d ms\n", delta_ms); + + timeout_ms -= delta_ms; + } + } +bail: + if (fds) + free(fds); + return rc; +} + +static int add_one_dev(struct netdev *dev) +{ + struct state *state; + + state = malloc(sizeof(struct state)); + if (!state) + return -1; + + state->dev = dev; + state->expire = time(NULL); + state->retry_period = 1; + + /* + * Select the state that we start from. + */ + if (dev->caps & CAP_DHCP && dev->ip_addr == INADDR_ANY) + state->restart_state = state->state = DEVST_DHCPDISC; + else if (dev->caps & CAP_DHCP) + state->restart_state = state->state = DEVST_DHCPREQ; + else if (dev->caps & CAP_BOOTP) + state->restart_state = state->state = DEVST_BOOTP; + + state->next = slist; + slist = state; + + n_devices++; + + return 0; +} + +static void parse_addr(uint32_t *addr, const char *ip) +{ + struct in_addr in; + if (inet_aton(ip, &in) == 0) { + fprintf(stderr, "%s: can't parse IP address '%s'\n", + progname, ip); + longjmp(abort_buf, 1); + } + *addr = in.s_addr; +} + +static unsigned int parse_proto(const char *ip) +{ + unsigned int caps = 0; + + if (*ip == '\0' || strcmp(ip, "on") == 0 || strcmp(ip, "any") == 0) + caps = CAP_BOOTP | CAP_DHCP | CAP_RARP; + else if (strcmp(ip, "both") == 0) + caps = CAP_BOOTP | CAP_RARP; + else if (strcmp(ip, "dhcp") == 0) + caps = CAP_BOOTP | CAP_DHCP; + else if (strcmp(ip, "bootp") == 0) + caps = CAP_BOOTP; + else if (strcmp(ip, "rarp") == 0) + caps = CAP_RARP; + else if (strcmp(ip, "none") == 0 || strcmp(ip, "static") == 0 + || strcmp(ip, "off") == 0) + goto bail; + else { + fprintf(stderr, "%s: invalid protocol '%s'\n", progname, ip); + longjmp(abort_buf, 1); + } +bail: + return caps; +} + +static int add_all_devices(struct netdev *template); + +static int parse_device(struct netdev *dev, char *ip) +{ + char *cp; + int opt; + int is_ip = 0; + + dprintf("IP-Config: parse_device: \"%s\"\n", ip); + + if (strncmp(ip, "ip=", 3) == 0) { + ip += 3; + is_ip = 1; + } else if (strncmp(ip, "nfsaddrs=", 9) == 0) { + ip += 9; + is_ip = 1; /* Not sure about this...? */ + } + + if (!strchr(ip, ':')) { + /* Only one option, e.g. "ip=dhcp", or an interface name */ + if (is_ip) { + dev->caps = parse_proto(ip); + bringup_first = 1; + } else { + dev->name = ip; + } + } else { + for (opt = 0; ip && *ip; ip = cp, opt++) { + if ((cp = strchr(ip, ':'))) { + *cp++ = '\0'; + } + if (*ip == '\0') + continue; + dprintf("IP-Config: opt #%d: '%s'\n", opt, ip); + switch (opt) { + case 0: + parse_addr(&dev->ip_addr, ip); + dev->caps = 0; + break; + case 1: + parse_addr(&dev->ip_server, ip); + break; + case 2: + parse_addr(&dev->ip_gateway, ip); + break; + case 3: + parse_addr(&dev->ip_netmask, ip); + break; + case 4: + strncpy(dev->hostname, ip, SYS_NMLN - 1); + dev->hostname[SYS_NMLN - 1] = '\0'; + memcpy(dev->reqhostname, dev->hostname, + SYS_NMLN); + break; + case 5: + dev->name = ip; + break; + case 6: + dev->caps = parse_proto(ip); + break; + case 7: + parse_addr(&dev->ip_nameserver[0], ip); + break; + case 8: + parse_addr(&dev->ip_nameserver[1], ip); + break; + case 9: + /* NTP server - ignore */ + break; + } + } + } + + if (dev->name == NULL || + dev->name[0] == '\0' || strcmp(dev->name, "all") == 0) { + add_all_devices(dev); + bringup_first = 1; + return 0; + } + return 1; +} + +static void bringup_device(struct netdev *dev) +{ + if (netdev_up(dev) == 0) { + if (dev->caps) + add_one_dev(dev); + else { + dev->proto = PROTO_NONE; + complete_device(dev); + } + } +} + +static void bringup_one_dev(struct netdev *template, struct netdev *dev) +{ + if (template->ip_addr != INADDR_NONE) + dev->ip_addr = template->ip_addr; + if (template->ip_server != INADDR_NONE) + dev->ip_server = template->ip_server; + if (template->ip_gateway != INADDR_NONE) + dev->ip_gateway = template->ip_gateway; + if (template->ip_netmask != INADDR_NONE) + dev->ip_netmask = template->ip_netmask; + if (template->ip_nameserver[0] != INADDR_NONE) + dev->ip_nameserver[0] = template->ip_nameserver[0]; + if (template->ip_nameserver[1] != INADDR_NONE) + dev->ip_nameserver[1] = template->ip_nameserver[1]; + if (template->hostname[0] != '\0') + strcpy(dev->hostname, template->hostname); + if (template->reqhostname[0] != '\0') + strcpy(dev->reqhostname, template->reqhostname); + dev->caps &= template->caps; + + bringup_device(dev); +} + +static struct netdev *add_device(char *info) +{ + struct netdev *dev; + int i; + + dev = malloc(sizeof(struct netdev)); + if (dev == NULL) { + fprintf(stderr, "%s: out of memory\n", progname); + longjmp(abort_buf, 1); + } + + memset(dev, 0, sizeof(struct netdev)); + dev->caps = default_caps; + + if (parse_device(dev, info) == 0) + goto bail; + + if (netdev_init_if(dev) == -1) + goto bail; + + if (bootp_init_if(dev) == -1) + goto bail; + + if (packet_open(dev) == -1) + goto bail; + + printf("IP-Config: %s hardware address", dev->name); + for (i = 0; i < dev->hwlen; i++) + printf("%c%02x", i == 0 ? ' ' : ':', dev->hwaddr[i]); + printf(" mtu %d%s%s\n", dev->mtu, + dev->caps & CAP_DHCP ? " DHCP" : + dev->caps & CAP_BOOTP ? " BOOTP" : "", + dev->caps & CAP_RARP ? " RARP" : ""); + return dev; +bail: + free(dev); + return NULL; +} + +static int add_all_devices(struct netdev *template) +{ + DIR *d; + struct dirent *de; + struct netdev *dev; + char t[PATH_MAX], p[255]; + int i, fd; + unsigned long flags; + + d = opendir(sysfs_class_net); + if (!d) + return 0; + + while ((de = readdir(d)) != NULL) { + /* This excludes devices beginning with dots or "dummy", + as well as . or .. */ + if (de->d_name[0] == '.' || !strcmp(de->d_name, "..")) + continue; + i = snprintf(t, PATH_MAX - 1, "%s/%s/flags", sysfs_class_net, + de->d_name); + if (i < 0 || i >= PATH_MAX - 1) + continue; + t[i] = '\0'; + fd = open(t, O_RDONLY); + if (fd < 0) { + perror(t); + continue; + } + i = read(fd, &p, sizeof(p) - 1); + close(fd); + if (i < 0) { + perror(t); + continue; + } + p[i] = '\0'; + flags = strtoul(p, NULL, 0); + /* Heuristic for if this is a reasonable boot interface. + This is the same + logic the in-kernel ipconfig uses... */ + if (!(flags & IFF_LOOPBACK) && + (flags & (IFF_BROADCAST | IFF_POINTOPOINT))) { + dprintf("Trying to bring up %s\n", de->d_name); + + dev = add_device(de->d_name); + if (!dev) + continue; + bringup_one_dev(template, dev); + } + } + closedir(d); + return 1; +} + +static int check_autoconfig(void) +{ + int ndev = 0, nauto = 0; + struct state *s; + + for (s = slist; s; s = s->next) { + ndev++; + if (s->dev->caps) + nauto++; + } + + if (ndev == 0) { + if (configured == 0) { + fprintf(stderr, "%s: no devices to configure\n", + progname); + longjmp(abort_buf, 1); + } + } + + return nauto; +} + +static void set_vendor_identifier(const char *id) +{ + int len = strlen(id); + if (len >= 255) { + fprintf(stderr, + "%s: invalid vendor class identifier: " + "%s\n", progname, id); + longjmp(abort_buf, 1); + } + memcpy(vendor_class_identifier+2, id, len); + vendor_class_identifier[0] = 60; + vendor_class_identifier[1] = len; + vendor_class_identifier_len = len+2; +} + +int main(int argc, char *argv[]) + __attribute__ ((weak, alias("ipconfig_main"))); + +int ipconfig_main(int argc, char *argv[]) +{ + struct netdev *dev; + int c, port; + int err = 0; + + /* If progname is set we're invoked from another program */ + if (!progname) { + struct timeval now; + progname = argv[0]; + gettimeofday(&now, NULL); + srand48(now.tv_usec ^ (now.tv_sec << 24)); + } + + if ((err = setjmp(abort_buf))) + return err; + + /* Default vendor identifier */ + set_vendor_identifier("Linux ipconfig"); + + do { + c = getopt(argc, argv, "c:d:i:onp:t:"); + if (c == EOF) + break; + + switch (c) { + case 'c': + default_caps = parse_proto(optarg); + break; + case 'p': + port = atoi(optarg); + if (port <= 0 || port > USHRT_MAX) { + fprintf(stderr, + "%s: invalid port number %d\n", + progname, port); + longjmp(abort_buf, 1); + } + cfg_local_port = port; + cfg_remote_port = cfg_local_port - 1; + break; + case 't': + loop_timeout = atoi(optarg); + if (loop_timeout < 0) { + fprintf(stderr, + "%s: invalid timeout %d\n", + progname, loop_timeout); + longjmp(abort_buf, 1); + } + break; + case 'i': + set_vendor_identifier(optarg); + break; + case 'o': + bringup_first = 1; + break; + case 'n': + do_not_config = 1; + break; + case 'd': + dev = add_device(optarg); + if (dev) + bringup_device(dev); + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + longjmp(abort_buf, 1); + } + } while (1); + + for (c = optind; c < argc; c++) { + dev = add_device(argv[c]); + if (dev) + bringup_device(dev); + } + + if (check_autoconfig()) { + if (cfg_local_port != LOCAL_PORT) { + printf("IP-Config: binding source port to %d, " + "dest to %d\n", + cfg_local_port, cfg_remote_port); + } + err = loop(); + } + + return err; +} diff --git a/usr/kinit/ipconfig/netdev.c b/usr/kinit/ipconfig/netdev.c new file mode 100644 index 0000000..de87f96 --- /dev/null +++ b/usr/kinit/ipconfig/netdev.c @@ -0,0 +1,279 @@ +/* + * ioctl-based device configuration + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <linux/route.h> +#include <linux/sockios.h> + +#include "netdev.h" + +static int cfd = -1; + +static void copy_name(struct netdev *dev, struct ifreq *ifr) +{ + strncpy(ifr->ifr_name, dev->name, sizeof(ifr->ifr_name)); + ifr->ifr_name[sizeof(ifr->ifr_name) - 1] = '\0'; +} + +int netdev_getflags(struct netdev *dev, short *flags) +{ + struct ifreq ifr; + + copy_name(dev, &ifr); + + if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) { + perror("SIOCGIFFLAGS"); + return -1; + } + + *flags = ifr.ifr_flags; + return 0; +} + +static int netdev_sif_addr(struct ifreq *ifr, int cmd, uint32_t addr) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = addr; + + memcpy(&ifr->ifr_addr, &sin, sizeof sin); + + return ioctl(cfd, cmd, ifr); +} + +int netdev_setaddress(struct netdev *dev) +{ + struct ifreq ifr; + + copy_name(dev, &ifr); + + if (dev->ip_addr != INADDR_ANY && + netdev_sif_addr(&ifr, SIOCSIFADDR, dev->ip_addr) == -1) { + perror("SIOCSIFADDR"); + return -1; + } + + if (dev->ip_broadcast != INADDR_ANY && + netdev_sif_addr(&ifr, SIOCSIFBRDADDR, dev->ip_broadcast) == -1) { + perror("SIOCSIFBRDADDR"); + return -1; + } + + if (dev->ip_netmask != INADDR_ANY && + netdev_sif_addr(&ifr, SIOCSIFNETMASK, dev->ip_netmask) == -1) { + perror("SIOCSIFNETMASK"); + return -1; + } + + return 0; +} + +static void set_s_addr(struct sockaddr *saddr, uint32_t ipaddr) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = ipaddr, + }; + memcpy(saddr, &sin, sizeof sin); +} + +int netdev_setroutes(struct netdev *dev) +{ + struct rtentry r; + + /* RFC3442 demands: + If the DHCP server returns both a Classless Static Routes option and + a Router option, the DHCP client MUST ignore the Router option. */ + if (dev->routes != NULL) { + struct route *cur; + for (cur = dev->routes; cur != NULL; cur = cur->next) { + memset(&r, 0, sizeof(r)); + + r.rt_dev = dev->name; + set_s_addr(&r.rt_dst, cur->subnet); + set_s_addr(&r.rt_gateway, cur->gateway); + set_s_addr(&r.rt_genmask, netdev_genmask(cur->netmask_width)); + r.rt_flags = RTF_UP; + if (cur->gateway != 0) { + r.rt_flags |= RTF_GATEWAY; + } + + if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) { + perror("SIOCADDRT"); + return -1; + } + } + } else if (dev->ip_gateway != INADDR_ANY) { + memset(&r, 0, sizeof(r)); + + set_s_addr(&r.rt_dst, INADDR_ANY); + set_s_addr(&r.rt_gateway, dev->ip_gateway); + set_s_addr(&r.rt_genmask, INADDR_ANY); + r.rt_flags = RTF_UP | RTF_GATEWAY; + + if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) { + perror("SIOCADDRT"); + return -1; + } + } + return 0; +} + +int netdev_setmtu(struct netdev *dev) +{ + struct ifreq ifr; + + copy_name(dev, &ifr); + ifr.ifr_mtu = dev->mtu; + + return ioctl(cfd, SIOCSIFMTU, &ifr); +} + +static int netdev_gif_addr(struct ifreq *ifr, int cmd, uint32_t * ptr) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; + + if (ioctl(cfd, cmd, ifr) == -1) + return -1; + + *ptr = sin->sin_addr.s_addr; + + return 0; +} + +int netdev_up(struct netdev *dev) +{ + struct ifreq ifr; + + copy_name(dev, &ifr); + + if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) { + perror("SIOCGIFFLAGS"); + return -1; + } + + ifr.ifr_flags |= IFF_UP; + + if (ioctl(cfd, SIOCSIFFLAGS, &ifr) == -1) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + +int netdev_down(struct netdev *dev) +{ + struct ifreq ifr; + + copy_name(dev, &ifr); + + if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) { + perror("SIOCGIFFLAGS"); + return -1; + } + + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(cfd, SIOCSIFFLAGS, &ifr) == -1) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + +int netdev_init_if(struct netdev *dev) +{ + struct ifreq ifr; + + if (cfd == -1) + cfd = socket(AF_INET, SOCK_DGRAM, 0); + if (cfd == -1) { + fprintf(stderr, "ipconfig: %s: socket(AF_INET): %s\n", + dev->name, strerror(errno)); + return -1; + } + + copy_name(dev, &ifr); + + if (ioctl(cfd, SIOCGIFINDEX, &ifr) == -1) { + fprintf(stderr, "ipconfig: %s: SIOCGIFINDEX: %s\n", + dev->name, strerror(errno)); + return -1; + } + + dev->ifindex = ifr.ifr_ifindex; + + if (ioctl(cfd, SIOCGIFMTU, &ifr) == -1) { + fprintf(stderr, "ipconfig: %s: SIOCGIFMTU: %s\n", + dev->name, strerror(errno)); + return -1; + } + + dev->mtu = ifr.ifr_mtu; + + if (ioctl(cfd, SIOCGIFHWADDR, &ifr) == -1) { + fprintf(stderr, "ipconfig: %s: SIOCGIFHWADDR: %s\n", + dev->name, strerror(errno)); + return -1; + } + + dev->hwtype = ifr.ifr_hwaddr.sa_family; + dev->hwlen = 0; + + switch (dev->hwtype) { + case ARPHRD_ETHER: + dev->hwlen = 6; + break; + case ARPHRD_EUI64: + dev->hwlen = 8; + break; + case ARPHRD_LOOPBACK: + dev->hwlen = 0; + break; + default: + return -1; + } + + memcpy(dev->hwaddr, ifr.ifr_hwaddr.sa_data, dev->hwlen); + memset(dev->hwbrd, 0xff, dev->hwlen); + + /* + * Try to get the current interface information. + */ + if (dev->ip_addr == INADDR_NONE && + netdev_gif_addr(&ifr, SIOCGIFADDR, &dev->ip_addr) == -1) { + fprintf(stderr, "ipconfig: %s: SIOCGIFADDR: %s\n", + dev->name, strerror(errno)); + dev->ip_addr = 0; + dev->ip_broadcast = 0; + dev->ip_netmask = 0; + return 0; + } + + if (dev->ip_broadcast == INADDR_NONE && + netdev_gif_addr(&ifr, SIOCGIFBRDADDR, &dev->ip_broadcast) == -1) { + fprintf(stderr, "ipconfig: %s: SIOCGIFBRDADDR: %s\n", + dev->name, strerror(errno)); + dev->ip_broadcast = 0; + } + + if (dev->ip_netmask == INADDR_NONE && + netdev_gif_addr(&ifr, SIOCGIFNETMASK, &dev->ip_netmask) == -1) { + fprintf(stderr, "ipconfig: %s: SIOCGIFNETMASK: %s\n", + dev->name, strerror(errno)); + dev->ip_netmask = 0; + } + + return 0; +} diff --git a/usr/kinit/ipconfig/netdev.h b/usr/kinit/ipconfig/netdev.h new file mode 100644 index 0000000..dbc80cd --- /dev/null +++ b/usr/kinit/ipconfig/netdev.h @@ -0,0 +1,107 @@ +#ifndef IPCONFIG_NETDEV_H +#define IPCONFIG_NETDEV_H + +#include <arpa/inet.h> +#include <sys/utsname.h> +#include <net/if.h> + +#define BPLEN 256 +#define FNLEN 128 /* from DHCP RFC 2131 */ + +struct route { + uint32_t subnet; /* subnet */ + uint32_t netmask_width; /* subnet mask width */ + uint32_t gateway; /* gateway */ + struct route *next; +}; + +struct netdev { + char *name; /* Device name */ + unsigned int ifindex; /* interface index */ + unsigned int hwtype; /* ARPHRD_xxx */ + unsigned int hwlen; /* HW address length */ + uint8_t hwaddr[16]; /* HW address */ + uint8_t hwbrd[16]; /* Broadcast HW address */ + unsigned int mtu; /* Device mtu */ + unsigned int caps; /* Capabilities */ + time_t open_time; + + struct { /* BOOTP/DHCP info */ + int fd; + uint32_t xid; + uint32_t gateway; /* BOOTP/DHCP gateway */ + } bootp; + + struct { /* RARP information */ + int fd; + } rarp; + + uint8_t proto; /* a protocol used (e.g. PROTO_DHCP) */ + uint32_t ip_addr; /* my address */ + uint32_t ip_broadcast; /* broadcast address */ + uint32_t ip_server; /* server address */ + uint32_t ip_netmask; /* my subnet mask */ + uint32_t ip_gateway; /* my gateway */ + uint32_t ip_nameserver[2]; /* two nameservers */ + uint32_t serverid; /* dhcp serverid */ + uint32_t dhcpleasetime; /* duration in seconds */ + char reqhostname[SYS_NMLN]; /* requested hostname */ + char hostname[SYS_NMLN]; /* hostname */ + char dnsdomainname[SYS_NMLN]; /* dns domain name */ + char nisdomainname[SYS_NMLN]; /* nis domain name */ + char bootpath[BPLEN]; /* boot path */ + char filename[FNLEN]; /* filename */ + char *domainsearch; /* decoded, NULL or malloc-ed */ + struct route *routes; /* decoded, NULL or malloc-ed list */ + long uptime; /* when complete configuration */ + int pkt_fd; /* packet socket for this interface */ + struct netdev *next; /* next configured i/f */ +}; + +extern struct netdev *ifaces; + +/* + * Device capabilities + */ +#define CAP_BOOTP (1<<0) +#define CAP_DHCP (1<<1) +#define CAP_RARP (1<<2) + +/* + * Device states + */ +#define DEVST_UP 0 +#define DEVST_BOOTP 1 +#define DEVST_DHCPDISC 2 +#define DEVST_DHCPREQ 3 +#define DEVST_COMPLETE 4 +#define DEVST_ERROR 5 + +int netdev_getflags(struct netdev *dev, short *flags); +int netdev_setaddress(struct netdev *dev); +int netdev_setroutes(struct netdev *dev); +int netdev_up(struct netdev *dev); +int netdev_down(struct netdev *dev); +int netdev_init_if(struct netdev *dev); +int netdev_setmtu(struct netdev *dev); + +static inline int netdev_running(struct netdev *dev) +{ + short flags; + int ret = netdev_getflags(dev, &flags); + + return ret ? 0 : !!(flags & IFF_RUNNING); +} + +static inline uint32_t netdev_genmask(uint32_t netmask_width) +{ + /* Map netmask width to network mask in network byte order. + Example: 24 -> "255.255.255.0" -> htonl(0xFFFFFF00) */ + if (netmask_width == 0) { + return 0; + } else { + return htonl(~((1u << (32 - netmask_width)) - 1)); + } +} + +#endif /* IPCONFIG_NETDEV_H */ diff --git a/usr/kinit/ipconfig/packet.c b/usr/kinit/ipconfig/packet.c new file mode 100644 index 0000000..2e1487d --- /dev/null +++ b/usr/kinit/ipconfig/packet.c @@ -0,0 +1,278 @@ +#include <errno.h>/*XXX*/ +/* + * Packet socket handling glue. + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <net/if_packet.h> +#include <netinet/if_ether.h> +#include <netinet/in.h> +#include <netpacket/packet.h> +#include <asm/byteorder.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <netinet/udp.h> + +#include "ipconfig.h" +#include "netdev.h" +#include "packet.h" + +uint16_t cfg_local_port = LOCAL_PORT; +uint16_t cfg_remote_port = REMOTE_PORT; + +int packet_open(struct netdev *dev) +{ + struct sockaddr_ll sll; + int fd, rv, one = 1; + + /* + * Get a PACKET socket for IP traffic. + */ + fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd == -1) { + perror("socket"); + return -1; + } + + /* + * We want to broadcast + */ + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, + sizeof(one)) == -1) { + perror("SO_BROADCAST"); + close(fd); + return -1; + } + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = dev->ifindex; + + rv = bind(fd, (struct sockaddr *)&sll, sizeof(sll)); + if (-1 == rv) { + perror("bind"); + close(fd); + return -1; + } + + dev->pkt_fd = fd; + return fd; +} + +void packet_close(struct netdev *dev) +{ + close(dev->pkt_fd); + dev->pkt_fd = -1; +} + +static unsigned int ip_checksum(uint16_t *hdr, int len) +{ + unsigned int chksum = 0; + + while (len) { + chksum += *hdr++; + chksum += *hdr++; + len--; + } + chksum = (chksum & 0xffff) + (chksum >> 16); + chksum = (chksum & 0xffff) + (chksum >> 16); + return (~chksum) & 0xffff; +} + +struct header { + struct iphdr ip; + struct udphdr udp; +} __attribute__ ((packed, aligned(4))); + +static struct header ipudp_hdrs = { + .ip = { + .ihl = 5, + .version = IPVERSION, + .frag_off = __constant_htons(IP_DF), + .ttl = 64, + .protocol = IPPROTO_UDP, + .saddr = INADDR_ANY, + .daddr = INADDR_BROADCAST, + }, + .udp = { + .source = __constant_htons(LOCAL_PORT), + .dest = __constant_htons(REMOTE_PORT), + .len = 0, + .check = 0, + }, +}; + +#ifdef DEBUG /* Only used with dprintf() */ +static char *ntoa(uint32_t addr) +{ + struct in_addr in = { addr }; + return inet_ntoa(in); +} +#endif /* DEBUG */ + +/* + * Send a packet. The options are listed in iov[1...iov_len-1]. + * iov[0] is reserved for the bootp packet header. + */ +int packet_send(struct netdev *dev, struct iovec *iov, int iov_len) +{ + struct sockaddr_ll sll; + struct msghdr msg; + int i, len = 0; + + memset(&sll, 0, sizeof(sll)); + msg.msg_name = &sll; + msg.msg_namelen = sizeof(sll); + msg.msg_iov = iov; + msg.msg_iovlen = iov_len; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (cfg_local_port != LOCAL_PORT) { + ipudp_hdrs.udp.source = htons(cfg_local_port); + ipudp_hdrs.udp.dest = htons(cfg_remote_port); + } + + dprintf("\n udp src %d dst %d", ntohs(ipudp_hdrs.udp.source), + ntohs(ipudp_hdrs.udp.dest)); + + dprintf("\n ip src %s ", ntoa(ipudp_hdrs.ip.saddr)); + dprintf("dst %s ", ntoa(ipudp_hdrs.ip.daddr)); + + /* + * Glue in the ip+udp header iovec + */ + iov[0].iov_base = &ipudp_hdrs; + iov[0].iov_len = sizeof(struct header); + + for (i = 0; i < iov_len; i++) + len += iov[i].iov_len; + + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_IP); + sll.sll_ifindex = dev->ifindex; + sll.sll_hatype = dev->hwtype; + sll.sll_pkttype = PACKET_BROADCAST; + sll.sll_halen = dev->hwlen; + memcpy(sll.sll_addr, dev->hwbrd, dev->hwlen); + + ipudp_hdrs.ip.tot_len = htons(len); + ipudp_hdrs.ip.check = 0; + ipudp_hdrs.ip.check = ip_checksum((uint16_t *) &ipudp_hdrs.ip, + ipudp_hdrs.ip.ihl); + + ipudp_hdrs.udp.len = htons(len - sizeof(struct iphdr)); + + dprintf("\n bytes %d\n", len); + + return sendmsg(dev->pkt_fd, &msg, 0); +} + +void packet_discard(struct netdev *dev) +{ + struct iphdr iph; + struct sockaddr_ll sll; + socklen_t sllen = sizeof(sll); + + sll.sll_ifindex = dev->ifindex; + + recvfrom(dev->pkt_fd, &iph, sizeof(iph), 0, + (struct sockaddr *)&sll, &sllen); +} + +/* + * Receive a bootp packet. The options are listed in iov[1...iov_len]. + * iov[0] must point to the bootp packet header. + * Returns: + * -1 = Error, try again later +* 0 = Discarded packet (non-DHCP/BOOTP traffic) + * >0 = Size of packet + */ +int packet_recv(struct netdev *dev, struct iovec *iov, int iov_len) +{ + struct iphdr *ip, iph; + struct udphdr *udp; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = iov, + .msg_iovlen = iov_len, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 + }; + int ret, iphl; + struct sockaddr_ll sll; + socklen_t sllen = sizeof(sll); + + sll.sll_ifindex = dev->ifindex; + msg.msg_name = &sll; + msg.msg_namelen = sllen; + + ret = recvfrom(dev->pkt_fd, &iph, sizeof(struct iphdr), + MSG_PEEK, (struct sockaddr *)&sll, &sllen); + if (ret == -1) + return -1; + + if (iph.ihl < 5 || iph.version != IPVERSION) + goto discard_pkt; + + iphl = iph.ihl * 4; + + ip = malloc(iphl + sizeof(struct udphdr)); + if (!ip) + goto discard_pkt; + + udp = (struct udphdr *)((char *)ip + iphl); + + iov[0].iov_base = ip; + iov[0].iov_len = iphl + sizeof(struct udphdr); + + ret = recvmsg(dev->pkt_fd, &msg, 0); + if (ret == -1) + goto free_pkt; + + dprintf("<- bytes %d ", ret); + + if (ip_checksum((uint16_t *) ip, ip->ihl) != 0) + goto free_pkt; + + dprintf("\n ip src %s ", ntoa(ip->saddr)); + dprintf("dst %s ", ntoa(ip->daddr)); + + if (ntohs(ip->tot_len) > ret || ip->protocol != IPPROTO_UDP) + goto free_pkt; + + ret -= 4 * ip->ihl; + + dprintf("\n udp src %d dst %d ", ntohs(udp->source), + ntohs(udp->dest)); + + if (udp->source != htons(cfg_remote_port) || + udp->dest != htons(cfg_local_port)) + goto free_pkt; + + if (ntohs(udp->len) > ret) + goto free_pkt; + + ret -= sizeof(struct udphdr); + + free(ip); + + return ret; + +free_pkt: + dprintf("freed\n"); + free(ip); + return 0; + +discard_pkt: + dprintf("discarded\n"); + packet_discard(dev); + return 0; +} diff --git a/usr/kinit/ipconfig/packet.h b/usr/kinit/ipconfig/packet.h new file mode 100644 index 0000000..4367efe --- /dev/null +++ b/usr/kinit/ipconfig/packet.h @@ -0,0 +1,12 @@ +#ifndef IPCONFIG_PACKET_H +#define IPCONFIG_PACKET_H + +struct iovec; + +int packet_open(struct netdev *dev); +void packet_close(struct netdev *dev); +int packet_send(struct netdev *dev, struct iovec *iov, int iov_len); +void packet_discard(struct netdev *dev); +int packet_recv(struct netdev *dev, struct iovec *iov, int iov_len); + +#endif /* IPCONFIG_PACKET_H */ diff --git a/usr/kinit/kinit.c b/usr/kinit/kinit.c new file mode 100644 index 0000000..28d2953 --- /dev/null +++ b/usr/kinit/kinit.c @@ -0,0 +1,331 @@ +#include <sys/mount.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <alloca.h> +#include <limits.h> +#include <ctype.h> +#include <termios.h> + +#include "kinit.h" +#include "ipconfig.h" +#include "run-init.h" +#include "resume.h" + +const char *progname = "kinit"; +int mnt_procfs; +int mnt_sysfs; + +#ifdef DEBUG +void dump_args(int argc, char *argv[]) +{ + int i; + + printf(" argc == %d\n", argc); + + for (i = 0; i < argc; i++) + printf(" argv[%d]: \"%s\"\n", i, argv[i]); + + if (argv[argc] != NULL) + printf(" argv[%d]: \"%s\" (SHOULD BE NULL)\n", + argc, argv[argc]); +} +#endif /* DEBUG */ + + +static int do_ipconfig(int argc, char *argv[]) +{ + int i, a = 0; + char **args = alloca((argc + 3) * sizeof(char *)); + + if (!args) + return -1; + + args[a++] = (char *)"IP-Config"; + args[a++] = (char *)"-i"; + args[a++] = (char *)"Linux kinit"; + + dprintf("Running ipconfig\n"); + + for (i = 1; i < argc; i++) { + if (strncmp(argv[i], "ip=", 3) == 0 || + strncmp(argv[i], "nfsaddrs=", 9) == 0) { + args[a++] = argv[i]; + } + } + + if (a > 1) { + args[a] = NULL; + dump_args(a, args); + return ipconfig_main(a, args); + } + + return 0; +} + +static int split_cmdline(int cmdcmax, char *cmdv[], char *argv0, + char *cmdlines[], char *args[]) +{ + int was_space; + char c, *p; + int vmax = cmdcmax; + int v = 1; + int space; + + if (cmdv) + cmdv[0] = argv0; + + /* First, add the parsable command lines */ + + while (*cmdlines) { + p = *cmdlines++; + was_space = 1; + while (v < vmax) { + c = *p; + space = isspace(c); + if ((space || !c) && !was_space) { + if (cmdv) + *p = '\0'; + v++; + } else if (was_space) { + if (cmdv) + cmdv[v] = p; + } + + if (!c) + break; + + was_space = space; + p++; + } + } + + /* Second, add the explicit command line arguments */ + + while (*args && v < vmax) { + if (cmdv) + cmdv[v] = *args; + v++; + args++; + } + + if (cmdv) + cmdv[v] = NULL; + + return v; +} + +static int mount_sys_fs(const char *check, const char *fsname, + const char *fstype) +{ + struct stat st; + + if (stat(check, &st) == 0) + return 0; + + mkdir(fsname, 0555); + + if (mount("none", fsname, fstype, 0, NULL) == -1) { + fprintf(stderr, "%s: could not mount %s as %s\n", + progname, fsname, fstype); + return -1; + } + + return 1; +} + +static void check_path(const char *path) +{ + struct stat st; + + if (stat(path, &st) == -1) { + if (errno != ENOENT) { + perror("stat"); + exit(1); + } + if (mkdir(path, 0755) == -1) { + perror("mkdir"); + exit(1); + } + } else if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "%s: '%s' not a directory\n", progname, path); + exit(1); + } +} + +static const char *find_init(const char *root, const char *user) +{ + const char *init_paths[] = { + "/sbin/init", "/bin/init", "/etc/init", "/bin/sh", NULL + }; + const char **p; + const char *path; + + if (chdir(root)) { + perror("chdir"); + exit(1); + } + + if (user) + dprintf("Checking for init: %s\n", user); + + if (user && user[0] == '/' && !access(user+1, X_OK)) { + path = user; + } else { + for (p = init_paths; *p; p++) { + dprintf("Checking for init: %s\n", *p); + if (!access(*p+1, X_OK)) + break; + } + path = *p; + } + chdir("/"); + return path; +} + +/* This is the argc and argv we pass to init */ +const char *init_path; +int init_argc; +char **init_argv; + +extern ssize_t readfile(const char *, char **); + +int main(int argc, char *argv[]) +{ + char **cmdv, **args; + char *cmdlines[3]; + int i; + const char *errmsg; + int ret = 0; + int cmdc; + int fd; + struct timeval now; + + gettimeofday(&now, NULL); + srand48(now.tv_usec ^ (now.tv_sec << 24)); + + /* Default parameters for anything init-like we execute */ + init_argc = argc; + init_argv = alloca((argc+1)*sizeof(char *)); + memcpy(init_argv, argv, (argc+1)*sizeof(char *)); + + if ((fd = open("/dev/console", O_RDWR)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + + if (fd > STDERR_FILENO) + close(fd); + } + + mnt_procfs = mount_sys_fs("/proc/cmdline", "/proc", "proc") >= 0; + if (!mnt_procfs) { + ret = 1; + goto bail; + } + + mnt_sysfs = mount_sys_fs("/sys/bus", "/sys", "sysfs") >= 0; + if (!mnt_sysfs) { + ret = 1; + goto bail; + } + + /* Construct the effective kernel command line. The + effective kernel command line consists of /arch.cmd, if + it exists, /proc/cmdline, plus any arguments after an -- + argument on the proper command line, in that order. */ + + ret = readfile("/arch.cmd", &cmdlines[0]); + if (ret < 0) + cmdlines[0] = ""; + + ret = readfile("/proc/cmdline", &cmdlines[1]); + if (ret < 0) { + fprintf(stderr, "%s: cannot read /proc/cmdline\n", progname); + ret = 1; + goto bail; + } + + cmdlines[2] = NULL; + + /* Find an -- argument, and if so append to the command line */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + i++; + break; + } + } + args = &argv[i]; /* Points either to first argument past -- or + to the final NULL */ + + /* Count the number of arguments */ + cmdc = split_cmdline(INT_MAX, NULL, argv[0], cmdlines, args); + + /* Actually generate the cmdline array */ + cmdv = (char **)alloca((cmdc+1)*sizeof(char *)); + if (split_cmdline(cmdc, cmdv, argv[0], cmdlines, args) != cmdc) { + ret = 1; + goto bail; + } + + /* Debugging... */ + dump_args(cmdc, cmdv); + + /* Resume from suspend-to-disk, if appropriate */ + /* If successful, does not return */ + do_resume(cmdc, cmdv); + + /* Initialize networking, if applicable */ + do_ipconfig(cmdc, cmdv); + + check_path("/root"); + do_mounts(cmdc, cmdv); + + if (mnt_procfs) { + umount2("/proc", 0); + mnt_procfs = 0; + } + + if (mnt_sysfs) { + umount2("/sys", 0); + mnt_sysfs = 0; + } + + init_path = find_init("/root", get_arg(cmdc, cmdv, "init=")); + if (!init_path) { + fprintf(stderr, "%s: init not found!\n", progname); + ret = 2; + goto bail; + } + + init_argv[0] = strrchr(init_path, '/') + 1; + + errmsg = run_init("/root", "/dev/console", + get_arg(cmdc, cmdv, "drop_capabilities="), false, + false, init_path, init_argv); + + /* If run_init returned, something went bad */ + fprintf(stderr, "%s: %s: %s\n", progname, errmsg, strerror(errno)); + ret = 2; + goto bail; + +bail: + if (mnt_procfs) + umount2("/proc", 0); + + if (mnt_sysfs) + umount2("/sys", 0); + + /* + * If we get here, something bad probably happened, and the kernel + * will most likely panic. Drain console output so the user can + * figure out what happened. + */ + tcdrain(2); + tcdrain(1); + + return ret; +} diff --git a/usr/kinit/kinit.h b/usr/kinit/kinit.h new file mode 100644 index 0000000..ee006f4 --- /dev/null +++ b/usr/kinit/kinit.h @@ -0,0 +1,70 @@ +/* + * kinit/kinit.h + */ + +#ifndef KINIT_H +#define KINIT_H + +#include <stddef.h> +#include <stdio.h> +#include <sys/types.h> + +int do_mounts(int argc, char *argv[]); +int mount_nfs_root(int argc, char *argv[], int flags); +int ramdisk_load(int argc, char *argv[]); +void md_run(int argc, char *argv[]); +const char *bdevname(dev_t dev); + +extern int mnt_procfs; +extern int mnt_sysfs; + +extern int init_argc; +extern char **init_argv; +extern const char *progname; + +char *get_arg(int argc, char *argv[], const char *name); +int get_flag(int argc, char *argv[], const char *name); + +int getintfile(const char *path, long *val); + +ssize_t readfile(const char *path, char **pptr); +ssize_t freadfile(FILE *f, char **pptr); + +/* + * min()/max() macros that also do + * strict type-checking.. See the + * "unnecessary" pointer comparison. + * From the Linux kernel. + */ +#define min(x, y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#define max(x, y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +#ifdef DEBUG +void dump_args(int argc, char *argv[]); +#else +static inline void dump_args(int argc, char *argv[]) +{ + (void)argc; + (void)argv; +} +#endif + +int drop_capabilities(const char *caps); + +#endif /* KINIT_H */ diff --git a/usr/kinit/name_to_dev.c b/usr/kinit/name_to_dev.c new file mode 100644 index 0000000..c57b7ce --- /dev/null +++ b/usr/kinit/name_to_dev.c @@ -0,0 +1,276 @@ +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <alloca.h> +#include <inttypes.h> + +#include "do_mounts.h" +#include "kinit.h" + +#define BUF_SZ 65536 + +/* Find dev_t for e.g. "hda,NULL" or "hdb,2" */ +static dev_t try_name(char *name, int part) +{ + char path[BUF_SZ]; + char buf[BUF_SZ]; + int range; + unsigned int major_num, minor_num; + dev_t res; + char *s; + int len; + int fd; + + /* read device number from /sys/block/.../dev */ + snprintf(path, sizeof(path), "/sys/block/%s/dev", name); + fd = open(path, 0, 0); + if (fd < 0) + goto fail; + len = read(fd, buf, BUF_SZ); + close(fd); + + if (len <= 0 || len == BUF_SZ || buf[len - 1] != '\n') + goto fail; + buf[len - 1] = '\0'; + major_num = strtoul(buf, &s, 10); + if (*s != ':') + goto fail; + minor_num = strtoul(s + 1, &s, 10); + if (*s) + goto fail; + res = makedev(major_num, minor_num); + + /* if it's there and we are not looking for a partition - that's it */ + if (!part) + return res; + + /* otherwise read range from .../range */ + snprintf(path, sizeof(path), "/sys/block/%s/range", name); + fd = open(path, 0, 0); + if (fd < 0) + goto fail; + len = read(fd, buf, 32); + close(fd); + if (len <= 0 || len == 32 || buf[len - 1] != '\n') + goto fail; + buf[len - 1] = '\0'; + range = strtoul(buf, &s, 10); + if (*s) + goto fail; + + /* if partition is within range - we got it */ + if (part < range) { + dprintf("kinit: try_name %s,%d = %s\n", name, part, + bdevname(res + part)); + return res + part; + } + +fail: + return (dev_t) 0; +} + +/* + * Find dev_t for a block device based on the provided GPT partlabel. + * The partlabel to block device mapping is found by scanning all + * the entries in /sys/dev/block/, opening the uevent file and picking + * the device where the PARTNAME= entry matches partlabel. + */ +static dev_t partlabel_to_dev_t(const char *plabel) +{ + char path[BUF_SZ]; + DIR *dir; + FILE *fp; + struct dirent *dent; + char *ret; + char line[BUF_SZ]; + int match_label, major, minor; + + dir = opendir("/sys/dev/block"); + if (!dir) { + dprintf(stderr, "%s: error %i (%s) opening /sys/dev/block\n", + __func__, errno, strerror(errno)); + goto fail; + } + + while ((dent = readdir(dir)) != NULL) { + if (!strncmp(dent->d_name, ".", 1)) + continue; + snprintf(path, sizeof(path), "/sys/dev/block/%s/uevent", + dent->d_name); + + fp = fopen(path, "r"); + if (fp == NULL) { + dprintf(stderr, "kinit %s: error %i (%s) opening %s", + __func__, errno, strerror(errno), path); + continue; + } + + major = 0; + minor = 0; + match_label = 0; + while (!feof(fp)) { + ret = fgets(line, sizeof(line), fp); + if (ret == NULL) + continue; + if (!strncmp(line, "MAJOR=", 6)) + major = atoi(line+6); + if (!strncmp(line, "MINOR=", 6)) + minor = atoi(line+6); + if (!strncmp(line, "PARTNAME=", 9)) { + line[strcspn(line, "\n")] = 0; + if (!strncmp(line + 9, plabel, sizeof(line)-9)) + match_label = 1; + } + if (match_label && major && minor) { + fclose(fp); + closedir(dir); + return makedev(major, minor); + } + } + fclose(fp); + } + closedir(dir); + +fail: + return (dev_t) 0; +} + +/* + * Convert a name into device number. We accept the following variants: + * + * 1) device number in hexadecimal represents itself + * 2) device number in major:minor decimal represents itself + * 3) /dev/nfs represents Root_NFS + * 4) /dev/<disk_name> represents the device number of disk + * 5) /dev/<disk_name><decimal> represents the device number + * of partition - device number of disk plus the partition number + * 6) /dev/<disk_name>p<decimal> - same as the above, that form is + * used when disk name of partitioned disk ends on a digit. + * 7) an actual block device node in the initramfs filesystem + * 8) PARTLABEL=<name> with name being the GPT partition label. + * + * If name doesn't have fall into the categories above, we return 0. + * Driverfs is used to check if something is a disk name - it has + * all known disks under bus/block/devices. If the disk name + * contains slashes, name of driverfs node has them replaced with + * dots. try_name() does the actual checks, assuming that driverfs + * is mounted on rootfs /sys. + */ + +static inline dev_t name_to_dev_t_real(const char *name) +{ + char *p; + dev_t res = 0; + char *s; + int part; + struct stat st; + int len; + const char *devname; + char *cptr, *e1, *e2; + int major_num, minor_num; + + /* Are we a multi root line? */ + if (strchr(name, ',')) + return Root_MULTI; + + if (!strncmp(name, "PARTLABEL=", 10)) + return partlabel_to_dev_t(name + 10); + + if (name[0] == '/') { + devname = name; + } else { + char *dname = alloca(strlen(name) + 6); + sprintf(dname, "/dev/%s", name); + devname = dname; + } + + if (!stat(devname, &st) && S_ISBLK(st.st_mode)) + return st.st_rdev; + + if (strncmp(name, "/dev/", 5)) { + cptr = strchr(devname+5, ':'); + if (cptr && cptr[1] != '\0') { + /* Colon-separated decimal device number */ + *cptr = '\0'; + major_num = strtoul(devname+5, &e1, 10); + minor_num = strtoul(cptr+1, &e2, 10); + if (!*e1 && !*e2) + return makedev(major_num, minor_num); + *cptr = ':'; + } else { + /* Hexadecimal device number */ + res = (dev_t) strtoul(name, &p, 16); + if (!*p) + return res; + } + } else { + name += 5; + } + + if (!strcmp(name, "nfs")) + return Root_NFS; + + if (!strcmp(name, "ram")) /* /dev/ram - historic alias for /dev/ram0 */ + return Root_RAM0; + + if (!strncmp(name, "mtd", 3)) + return Root_MTD; + + len = strlen(name); + s = alloca(len + 1); + memcpy(s, name, len + 1); + + for (p = s; *p; p++) + if (*p == '/') + *p = '!'; + res = try_name(s, 0); + if (res) + return res; + + while (p > s && isdigit(p[-1])) + p--; + if (p == s || !*p || *p == '0') + goto fail; + part = strtoul(p, NULL, 10); + *p = '\0'; + res = try_name(s, part); + if (res) + return res; + + if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p') + goto fail; + p[-1] = '\0'; + res = try_name(s, part); + return res; + +fail: + return (dev_t) 0; +} + +dev_t name_to_dev_t(const char *name) +{ + dev_t dev = name_to_dev_t_real(name); + + dprintf("kinit: name_to_dev_t(%s) = %s\n", name, bdevname(dev)); + return dev; +} + +#ifdef TEST_NAMETODEV /* Standalone test */ + +int main(int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) + name_to_dev_t(argv[i]); + + return 0; +} + +#endif diff --git a/usr/kinit/nfsmount/Kbuild b/usr/kinit/nfsmount/Kbuild new file mode 100644 index 0000000..5f34950 --- /dev/null +++ b/usr/kinit/nfsmount/Kbuild @@ -0,0 +1,31 @@ +# +# kbuild file for nfsmount +# + +static-y := static/nfsmount +#FIXME - build is broken static-y := dummypmap +shared-y := shared/nfsmount + +objs := main.o mount.o portmap.o dummypmap.o sunrpc.o + +# Create built-in.o with all .o files (used by kinit) +lib-y := $(objs) + +# .o files used for executables +static/nfsmount-y := $(objs) +shared/nfsmount-y := $(objs) + +# dummypmap uses a single .o file (rename src file?) +dummypmap-y := dummypmap_test.o + +# TODO - do we want a stripped version +# TODO - do we want the static.g + shared.g directories? + +clean-dirs := static shared + +# Install binary +ifdef KLIBCSHAREDFLAGS +install-y := $(shared-y) +else +install-y := $(static-y) +endif diff --git a/usr/kinit/nfsmount/README.locking b/usr/kinit/nfsmount/README.locking new file mode 100644 index 0000000..bf2e8e7 --- /dev/null +++ b/usr/kinit/nfsmount/README.locking @@ -0,0 +1,26 @@ +I have implemented portmap spoofing in klibc nfsmount (released as +klibc-0.144) This is basically a vestigial portmap daemon which gets +launched before the mount() call and then just records any +transactions it gets to a file and sends back an affirmative reply. + +There are two ways to use it (this belongs in a README file, but it's +too late at night right now): + +a) Set a fixed portnumber in /proc/sys/nfs/nlm_tcpport and +/proc/sys/nfs/nlm_udpport before calling nfsmount; once the portmapper +starts feed that fixed portnumber to pmap_set(8). In this case the +pmap_file can be /dev/null. + +b) Allow the kernel to bind to any port and use the file produced by +nfsroot to feed to pmap_set (it should be directly compatible); this +means the file needs to be transferred to a place where the "real +root" can find it before run-init. + +In either case, it is imperative that the real portmapper is launched +before any program actually tries to create locks! + +To use it: + + # We need the loopback device to be up before we do this! + ipconfig 127.0.0.1:::::lo:none + nfsroot -p pmap_file -o lock server:/pathname /realpath diff --git a/usr/kinit/nfsmount/dummypmap.c b/usr/kinit/nfsmount/dummypmap.c new file mode 100644 index 0000000..07210c5 --- /dev/null +++ b/usr/kinit/nfsmount/dummypmap.c @@ -0,0 +1,281 @@ +/* + * Enough portmapper functionality that mount doesn't hang trying + * to start lockd. Enables nfsroot with locking functionality. + * + * Note: the kernel will only speak to the local portmapper + * using RPC over UDP. + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> + +#include "dummypmap.h" +#include "sunrpc.h" + +extern const char *progname; + +struct portmap_args { + uint32_t program; + uint32_t version; + uint32_t proto; + uint32_t port; +}; + +struct portmap_call { + struct rpc_call rpc; + struct portmap_args args; +}; + +struct portmap_reply { + struct rpc_reply rpc; + uint32_t port; +}; + +static int bind_portmap(void) +{ + int sock = socket(PF_INET, SOCK_DGRAM, 0); + struct sockaddr_in sin; + + if (sock < 0) + return -1; + + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ + sin.sin_port = htons(RPC_PMAP_PORT); + if (bind(sock, (struct sockaddr *)&sin, sizeof sin) < 0) { + int err = errno; + close(sock); + errno = err; + return -1; + } + + return sock; +} + +static const char *protoname(uint32_t proto) +{ + switch (ntohl(proto)) { + case IPPROTO_TCP: + return "tcp"; + case IPPROTO_UDP: + return "udp"; + default: + return NULL; + } +} + +static void *get_auth(struct rpc_auth *auth) +{ + switch (ntohl(auth->flavor)) { + case AUTH_NULL: + /* Fallthrough */ + case AUTH_UNIX: + return (char *)&auth->body + ntohl(auth->len); + default: + return NULL; + } +} + +static int check_unix_cred(struct rpc_auth *cred) +{ + uint32_t len; + int quad_len; + uint32_t node_name_len; + int quad_name_len; + uint32_t *base; + uint32_t *pos; + int ret = -1; + + len = ntohl(cred->len); + quad_len = (len + 3) >> 2; + if (quad_len < 6) + /* Malformed creds */ + goto out; + + base = pos = cred->body; + + /* Skip timestamp */ + pos++; + + /* Skip node name: only localhost can succeed. */ + node_name_len = ntohl(*pos++); + quad_name_len = (node_name_len + 3) >> 2; + if (pos + quad_name_len + 3 > base + quad_len) + /* Malformed creds */ + goto out; + pos += quad_name_len; + + /* uid must be 0 */ + if (*pos++ != 0) + goto out; + + /* gid must be 0 */ + if (*pos++ != 0) + goto out; + + /* Skip remaining gids */ + ret = 0; + +out: + return ret; +} + +static int check_cred(struct rpc_auth *cred) +{ + switch (ntohl(cred->flavor)) { + case AUTH_NULL: + return 0; + case AUTH_UNIX: + return check_unix_cred(cred); + default: + return -1; + } +} + +static int check_vrf(struct rpc_auth *vrf) +{ + return (vrf->flavor == htonl(AUTH_NULL)) ? 0 : -1; +} + +#define MAX_UDP_PACKET 65536 + +static int dummy_portmap(int sock, FILE *portmap_file) +{ + enum { PAYLOAD_SIZE = MAX_UDP_PACKET + offsetof(struct rpc_header, udp) }; + struct sockaddr_in sin; + int pktlen, addrlen; + union { + struct rpc_call rpc; + /* Max UDP packet size + unused TCP fragment size */ + char payload[PAYLOAD_SIZE]; + } pkt; + struct rpc_call *rpc = &pkt.rpc; + struct rpc_auth *cred; + struct rpc_auth *vrf; + struct portmap_args *args; + struct portmap_reply rply; + + for (;;) { + addrlen = sizeof sin; + pktlen = recvfrom(sock, &rpc->hdr.udp, MAX_UDP_PACKET, + 0, (struct sockaddr *)&sin, &addrlen); + + if (pktlen < 0) { + if (errno == EINTR) + continue; + + return -1; + } + + /* +4 to skip the TCP fragment header */ + if (pktlen + 4 < sizeof(struct portmap_call)) + continue; /* Bad packet */ + + if (rpc->hdr.udp.msg_type != htonl(RPC_CALL)) + continue; /* Bad packet */ + + memset(&rply, 0, sizeof rply); + + rply.rpc.hdr.udp.xid = rpc->hdr.udp.xid; + rply.rpc.hdr.udp.msg_type = htonl(RPC_REPLY); + + cred = (struct rpc_auth *) &rpc->cred_flavor; + if (rpc->rpc_vers != htonl(2)) { + rply.rpc.reply_state = htonl(REPLY_DENIED); + /* state <- RPC_MISMATCH == 0 */ + } else if (rpc->program != htonl(PORTMAP_PROGRAM)) { + rply.rpc.reply_state = htonl(PROG_UNAVAIL); + } else if (rpc->prog_vers != htonl(2)) { + rply.rpc.reply_state = htonl(PROG_MISMATCH); + } else if (!(vrf = get_auth(cred)) || + (char *)vrf > ((char *)&rpc->hdr.udp + pktlen - 8 - + sizeof(*args)) || + !(args = get_auth(vrf)) || + (char *)args > ((char *)&rpc->hdr.udp + pktlen - + sizeof(*args)) || + check_cred(cred) || check_vrf(vrf)) { + /* Can't deal with credentials data; the kernel + won't send them */ + rply.rpc.reply_state = htonl(SYSTEM_ERR); + } else { + switch (ntohl(rpc->proc)) { + case PMAP_PROC_NULL: + break; + case PMAP_PROC_SET: + if (args->proto == htonl(IPPROTO_TCP) || + args->proto == htonl(IPPROTO_UDP)) { + if (portmap_file) + fprintf(portmap_file, + "%u %u %s %u\n", + ntohl(args->program), + ntohl(args->version), + protoname(args->proto), + ntohl(args->port)); + rply.port = htonl(1); /* TRUE = success */ + } + break; + case PMAP_PROC_UNSET: + rply.port = htonl(1); /* TRUE = success */ + break; + case PMAP_PROC_GETPORT: + break; + case PMAP_PROC_DUMP: + break; + default: + rply.rpc.reply_state = htonl(PROC_UNAVAIL); + break; + } + } + + sendto(sock, &rply.rpc.hdr.udp, sizeof rply - 4, 0, + (struct sockaddr *)&sin, addrlen); + } +} + +pid_t start_dummy_portmap(const char *file) +{ + FILE *portmap_filep; + int sock; + pid_t spoof_portmap; + + portmap_filep = fopen(file, "w"); + if (!portmap_filep) { + fprintf(stderr, "%s: cannot write portmap file: %s\n", + progname, file); + return -1; + } + + sock = bind_portmap(); + if (sock == -1) { + if (errno == EINVAL || errno == EADDRINUSE) + return 0; /* Assume not needed */ + else { + fclose(portmap_filep); + fprintf(stderr, "%s: portmap spoofing failed\n", + progname); + return -1; + } + } + + spoof_portmap = fork(); + if (spoof_portmap == -1) { + fclose(portmap_filep); + fprintf(stderr, "%s: cannot fork\n", progname); + return -1; + } else if (spoof_portmap == 0) { + /* Child process */ + dummy_portmap(sock, portmap_filep); + _exit(255); /* Error */ + } else { + /* Parent process */ + close(sock); + return spoof_portmap; + } +} diff --git a/usr/kinit/nfsmount/dummypmap.h b/usr/kinit/nfsmount/dummypmap.h new file mode 100644 index 0000000..37650bf --- /dev/null +++ b/usr/kinit/nfsmount/dummypmap.h @@ -0,0 +1,11 @@ +/* + * Functions for the portmap spoofer + */ + +#ifndef NFSMOUNT_DUMMYPORTMAP_H +#define NFSMOUNT_DUMMYPORTMAP_H + +#include <unistd.h> +pid_t start_dummy_portmap(const char *file); + +#endif /* NFSMOUNT_DUMMYPORTMAP_H */ diff --git a/usr/kinit/nfsmount/dummypmap_test.c b/usr/kinit/nfsmount/dummypmap_test.c new file mode 100644 index 0000000..d81a141 --- /dev/null +++ b/usr/kinit/nfsmount/dummypmap_test.c @@ -0,0 +1,2 @@ +#define TEST +#include "dummypmap.c" diff --git a/usr/kinit/nfsmount/main.c b/usr/kinit/nfsmount/main.c new file mode 100644 index 0000000..66969f4 --- /dev/null +++ b/usr/kinit/nfsmount/main.c @@ -0,0 +1,288 @@ +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <setjmp.h> +#include <sys/wait.h> +#include <unistd.h> +#include <klibc/sysconfig.h> /* For _KLIBC_NO_MMU */ + +#include <linux/nfs_mount.h> + +#include "nfsmount.h" +#include "sunrpc.h" +#include "dummypmap.h" + +const char *progname; +static jmp_buf abort_buf; + +static struct nfs_mount_data mount_data = { + .version = NFS_MOUNT_VERSION, + .flags = NFS_MOUNT_NONLM | NFS_MOUNT_VER3 | NFS_MOUNT_TCP, + .rsize = 0, /* Server's choice */ + .wsize = 0, /* Server's choice */ + .timeo = 0, /* Kernel client's default */ + .retrans = 3, + .acregmin = 3, + .acregmax = 60, + .acdirmin = 30, + .acdirmax = 60, + .namlen = NAME_MAX, +}; + +int nfs_port; +int nfs_version; + +static struct int_opts { + char *name; + int *val; +} int_opts[] = { + {"port", &nfs_port}, + {"nfsvers", &nfs_version}, + {"vers", &nfs_version}, + {"rsize", &mount_data.rsize}, + {"wsize", &mount_data.wsize}, + {"timeo", &mount_data.timeo}, + {"retrans", &mount_data.retrans}, + {"acregmin", &mount_data.acregmin}, + {"acregmax", &mount_data.acregmax}, + {"acdirmin", &mount_data.acdirmin}, + {"acdirmax", &mount_data.acdirmax}, + {NULL, NULL} +}; + +static struct bool_opts { + char *name; + int and_mask; + int or_mask; +} bool_opts[] = { + {"soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT}, + {"hard", ~NFS_MOUNT_SOFT, 0}, + {"intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR}, + {"nointr", ~NFS_MOUNT_INTR, 0}, + {"posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX}, + {"noposix", ~NFS_MOUNT_POSIX, 0}, + {"cto", ~NFS_MOUNT_NOCTO, 0}, + {"nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO}, + {"ac", ~NFS_MOUNT_NOAC, 0}, + {"noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC}, + {"lock", ~NFS_MOUNT_NONLM, 0}, + {"nolock", ~NFS_MOUNT_NONLM, NFS_MOUNT_NONLM}, + {"acl", ~NFS_MOUNT_NOACL, 0}, + {"noacl", ~NFS_MOUNT_NOACL, NFS_MOUNT_NOACL}, + {"v2", ~NFS_MOUNT_VER3, 0}, + {"v3", ~NFS_MOUNT_VER3, NFS_MOUNT_VER3}, + {"udp", ~NFS_MOUNT_TCP, 0}, + {"tcp", ~NFS_MOUNT_TCP, NFS_MOUNT_TCP}, + {"broken_suid", ~NFS_MOUNT_BROKEN_SUID, NFS_MOUNT_BROKEN_SUID}, + {"ro", ~NFS_MOUNT_KLIBC_RONLY, NFS_MOUNT_KLIBC_RONLY}, + {"rw", ~NFS_MOUNT_KLIBC_RONLY, 0}, + {NULL, 0, 0} +}; + +static int parse_int(const char *val, const char *ctx) +{ + char *end; + int ret; + + ret = (int)strtoul(val, &end, 0); + if (*val == '\0' || *end != '\0') { + fprintf(stderr, "%s: invalid value for %s\n", val, ctx); + longjmp(abort_buf, 1); + } + return ret; +} + +static void parse_opts(char *opts) +{ + char *cp, *val; + + while ((cp = strsep(&opts, ",")) != NULL) { + if (*cp == '\0') + continue; + val = strchr(cp, '='); + if (val != NULL) { + struct int_opts *opts = int_opts; + *val++ = '\0'; + while (opts->name && strcmp(opts->name, cp) != 0) + opts++; + if (opts->name) + *(opts->val) = parse_int(val, opts->name); + else { + fprintf(stderr, "%s: bad option '%s'\n", + progname, cp); + longjmp(abort_buf, 1); + } + } else { + struct bool_opts *opts = bool_opts; + while (opts->name && strcmp(opts->name, cp) != 0) + opts++; + if (opts->name) { + mount_data.flags &= opts->and_mask; + mount_data.flags |= opts->or_mask; + } else { + fprintf(stderr, "%s: bad option '%s'\n", + progname, cp); + longjmp(abort_buf, 1); + } + } + } + /* If new-style options "nfsvers=" or "vers=" are passed, override + old "v2" and "v3" options */ + if (nfs_version != 0) { + switch (nfs_version) { + case 2: + mount_data.flags &= ~NFS_MOUNT_VER3; + break; + case 3: + mount_data.flags |= NFS_MOUNT_VER3; + break; + default: + fprintf(stderr, "%s: bad NFS version '%d'\n", + progname, nfs_version); + longjmp(abort_buf, 1); + } + } +} + +static uint32_t parse_addr(const char *ip) +{ + struct in_addr in; + if (inet_aton(ip, &in) == 0) { + fprintf(stderr, "%s: can't parse IP address '%s'\n", + progname, ip); + longjmp(abort_buf, 1); + } + return in.s_addr; +} + +static void check_path(const char *path) +{ + struct stat st; + + if (stat(path, &st) == -1) { + perror("stat"); + longjmp(abort_buf, 1); + } else if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "%s: '%s' not a directory\n", progname, path); + longjmp(abort_buf, 1); + } +} + +int main(int argc, char *argv[]) + __attribute__ ((weak, alias("nfsmount_main"))); + +int nfsmount_main(int argc, char *argv[]) +{ + uint32_t server; + char *rem_name; + char *rem_path; + char *hostname; + char *path; + int c; + const char *portmap_file; + pid_t spoof_portmap; + int err, ret; + + if ((err = setjmp(abort_buf))) + return err; + + /* Set these here to avoid longjmp warning */ + portmap_file = NULL; + spoof_portmap = 0; + server = 0; + + /* If progname is set we're invoked from another program */ + if (!progname) { + struct timeval now; + progname = argv[0]; + gettimeofday(&now, NULL); + srand48(now.tv_usec ^ (now.tv_sec << 24)); + } + + while ((c = getopt(argc, argv, "o:p:")) != EOF) { + switch (c) { + case 'o': + parse_opts(optarg); + break; + case 'p': + portmap_file = optarg; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + return 1; + } + } + + if (optind == argc) { + fprintf(stderr, "%s: need a path\n", progname); + return 1; + } + + hostname = rem_path = argv[optind]; + + rem_name = strdup(rem_path); + if (rem_name == NULL) { + perror("strdup"); + return 1; + } + + rem_path = strchr(rem_path, ':'); + if (rem_path == NULL) { + fprintf(stderr, "%s: need a server\n", progname); + free(rem_name); + return 1; + } + + *rem_path++ = '\0'; + + if (*rem_path != '/') { + fprintf(stderr, "%s: need a path\n", progname); + free(rem_name); + return 1; + } + + server = parse_addr(hostname); + + if (optind <= argc - 2) + path = argv[optind + 1]; + else + path = "/nfs_root"; + + check_path(path); + +#if !_KLIBC_NO_MMU + /* Note: uClinux can't fork(), so the spoof portmapper is not + available on uClinux. */ + if (portmap_file) + spoof_portmap = start_dummy_portmap(portmap_file); + + if (spoof_portmap == -1) { + free(rem_name); + return 1; + } +#endif + + ret = 0; + if (nfs_mount(rem_name, hostname, server, rem_path, path, + &mount_data) != 0) + ret = 1; + + /* If we set up the spoofer, tear it down now */ + if (spoof_portmap) { + kill(spoof_portmap, SIGTERM); + while (waitpid(spoof_portmap, NULL, 0) == -1 + && errno == EINTR) + ; + } + + free(rem_name); + + return ret; +} diff --git a/usr/kinit/nfsmount/mount.c b/usr/kinit/nfsmount/mount.c new file mode 100644 index 0000000..ae48354 --- /dev/null +++ b/usr/kinit/nfsmount/mount.c @@ -0,0 +1,347 @@ +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <linux/nfs.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "nfsmount.h" +#include "sunrpc.h" + +static uint32_t mount_port; + +struct mount_call { + struct rpc_call rpc; + uint32_t path_len; + char path[0]; +}; + +/* + * The following structure is the NFS v3 on-the-wire file handle, + * as defined in rfc1813. + * This differs from the structure used by the kernel, + * defined in <linux/nfh3.h>: rfc has a long in network order, + * kernel has a short in native order. + * Both kernel and rfc use the name nfs_fh; kernel name is + * visible to user apps in some versions of libc. + * Use different name to avoid clashes. + */ +#define NFS_MAXFHSIZE_WIRE 64 +struct nfs_fh_wire { + uint32_t size; + char data[NFS_MAXFHSIZE_WIRE]; +} __attribute__ ((packed, aligned(4))); + +struct mount_reply { + struct rpc_reply reply; + uint32_t status; + struct nfs_fh_wire fh; +} __attribute__ ((packed, aligned(4))); + +#define MNT_REPLY_MINSIZE (sizeof(struct rpc_reply) + sizeof(uint32_t)) + +static int get_ports(uint32_t server, const struct nfs_mount_data *data) +{ + uint32_t nfs_ver, mount_ver; + uint32_t proto; + + if (data->flags & NFS_MOUNT_VER3) { + nfs_ver = NFS3_VERSION; + mount_ver = NFS_MNT3_VERSION; + } else { + nfs_ver = NFS2_VERSION; + mount_ver = NFS_MNT_VERSION; + } + + proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP; + + if (nfs_port == 0) { + nfs_port = portmap(server, NFS_PROGRAM, nfs_ver, proto); + if (nfs_port == 0) { + if (proto == IPPROTO_TCP) { + struct in_addr addr = { server }; + fprintf(stderr, "NFS over TCP not " + "available from %s\n", inet_ntoa(addr)); + return -1; + } + nfs_port = NFS_PORT; + } + } + + if (mount_port == 0) { + mount_port = portmap(server, NFS_MNT_PROGRAM, mount_ver, proto); + if (mount_port == 0) + mount_port = MOUNT_PORT; + } + return 0; +} + +static inline int pad_len(int len) +{ + return (len + 3) & ~3; +} + +static inline void dump_params(uint32_t server, + const char *path, + const struct nfs_mount_data *data) +{ + (void)server; + (void)path; + (void)data; + +#ifdef DEBUG + struct in_addr addr = { server }; + + printf("NFS params:\n"); + printf(" server = %s, path = \"%s\", ", inet_ntoa(addr), path); + printf("version = %d, proto = %s\n", + data->flags & NFS_MOUNT_VER3 ? 3 : 2, + (data->flags & NFS_MOUNT_TCP) ? "tcp" : "udp"); + printf(" mount_port = %d, nfs_port = %d, flags = %08x\n", + mount_port, nfs_port, data->flags); + printf(" rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", + data->rsize, data->wsize, data->timeo, data->retrans); + printf(" acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n", + data->acregmin, data->acregmax, data->acdirmin, data->acdirmax); + printf(" soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n", + (data->flags & NFS_MOUNT_SOFT) != 0, + (data->flags & NFS_MOUNT_INTR) != 0, + (data->flags & NFS_MOUNT_POSIX) != 0, + (data->flags & NFS_MOUNT_NOCTO) != 0, + (data->flags & NFS_MOUNT_NOAC) != 0); +#endif +} + +static inline void dump_fh(const char *data, int len) +{ + (void)data; + (void)len; + +#ifdef DEBUG + int i = 0; + int max = len - (len % 8); + + printf("Root file handle: %d bytes\n", NFS2_FHSIZE); + + while (i < max) { + int j; + + printf(" %4d: ", i); + for (j = 0; j < 4; j++) { + printf("%02x %02x %02x %02x ", + data[i] & 0xff, data[i + 1] & 0xff, + data[i + 2] & 0xff, data[i + 3] & 0xff); + } + i += j; + printf("\n"); + } +#endif +} + +static struct mount_reply mnt_reply; + +static int mount_call(uint32_t proc, uint32_t version, + const char *path, struct client *clnt) +{ + struct mount_call *mnt_call = NULL; + size_t path_len, call_len; + struct rpc rpc; + int ret = 0; + + path_len = strlen(path); + call_len = sizeof(*mnt_call) + pad_len(path_len); + + mnt_call = malloc(call_len); + if (mnt_call == NULL) { + perror("malloc"); + goto bail; + } + + memset(mnt_call, 0, sizeof(*mnt_call)); + + mnt_call->rpc.program = htonl(NFS_MNT_PROGRAM); + mnt_call->rpc.prog_vers = htonl(version); + mnt_call->rpc.proc = htonl(proc); + mnt_call->path_len = htonl(path_len); + memcpy(mnt_call->path, path, path_len); + + rpc.call = (struct rpc_call *)mnt_call; + rpc.call_len = call_len; + rpc.reply = (struct rpc_reply *)&mnt_reply; + rpc.reply_len = sizeof(mnt_reply); + + if (rpc_call(clnt, &rpc) < 0) + goto bail; + + if (proc != MNTPROC_MNT) + goto done; + + if (rpc.reply_len < MNT_REPLY_MINSIZE) { + fprintf(stderr, "incomplete reply: %zu < %zu\n", + rpc.reply_len, MNT_REPLY_MINSIZE); + goto bail; + } + + if (mnt_reply.status != 0) { + fprintf(stderr, "mount call failed - server replied: %s.\n", + strerror(ntohl(mnt_reply.status))); + goto bail; + } + + goto done; + +bail: + ret = -1; + +done: + if (mnt_call) + free(mnt_call); + + return ret; +} + +static int mount_v2(const char *path, + struct nfs_mount_data *data, struct client *clnt) +{ + int ret = mount_call(MNTPROC_MNT, NFS_MNT_VERSION, path, clnt); + + if (ret == 0) { + dump_fh((const char *)&mnt_reply.fh, NFS2_FHSIZE); + + data->root.size = NFS_FHSIZE; + memcpy(data->root.data, &mnt_reply.fh, NFS_FHSIZE); + memcpy(data->old_root.data, &mnt_reply.fh, NFS_FHSIZE); + } + + return ret; +} + +static inline int umount_v2(const char *path, struct client *clnt) +{ + return mount_call(MNTPROC_UMNT, NFS_MNT_VERSION, path, clnt); +} + +static int mount_v3(const char *path, + struct nfs_mount_data *data, struct client *clnt) +{ + int ret = mount_call(MNTPROC_MNT, NFS_MNT3_VERSION, path, clnt); + + if (ret == 0) { + size_t fhsize = ntohl(mnt_reply.fh.size); + + dump_fh((const char *)&mnt_reply.fh.data, fhsize); + + memset(data->old_root.data, 0, NFS_FHSIZE); + memset(&data->root, 0, sizeof(data->root)); + data->root.size = fhsize; + memcpy(&data->root.data, mnt_reply.fh.data, fhsize); + data->flags |= NFS_MOUNT_VER3; + } + + return ret; +} + +static inline int umount_v3(const char *path, struct client *clnt) +{ + return mount_call(MNTPROC_UMNT, NFS_MNT3_VERSION, path, clnt); +} + +int nfs_mount(const char *pathname, const char *hostname, + uint32_t server, const char *rem_path, const char *path, + struct nfs_mount_data *data) +{ + struct client *clnt = NULL; + struct sockaddr_in addr; + char mounted = 0; + int sock = -1; + int ret = 0; + int mountflags; + + if (get_ports(server, data) != 0) + goto bail; + + dump_params(server, rem_path, data); + + if (data->flags & NFS_MOUNT_TCP) + clnt = tcp_client(server, mount_port, CLI_RESVPORT); + else + clnt = udp_client(server, mount_port, CLI_RESVPORT); + + if (clnt == NULL) + goto bail; + + if (data->flags & NFS_MOUNT_VER3) + ret = mount_v3(rem_path, data, clnt); + else + ret = mount_v2(rem_path, data, clnt); + + if (ret == -1) + goto bail; + mounted = 1; + + if (data->flags & NFS_MOUNT_TCP) + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + else + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (sock == -1) { + perror("socket"); + goto bail; + } + + if (bindresvport(sock, 0) == -1) { + perror("bindresvport"); + goto bail; + } + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = server; + addr.sin_port = htons(nfs_port); + memcpy(&data->addr, &addr, sizeof(data->addr)); + + strncpy(data->hostname, hostname, sizeof(data->hostname)); + + data->fd = sock; + + mountflags = (data->flags & NFS_MOUNT_KLIBC_RONLY) ? MS_RDONLY : 0; + data->flags = data->flags & NFS_MOUNT_FLAGMASK; + ret = mount(pathname, path, "nfs", mountflags, data); + + if (ret == -1) { + if (errno == ENODEV) { + fprintf(stderr, "mount: the kernel lacks NFS v%d " + "support\n", + (data->flags & NFS_MOUNT_VER3) ? 3 : 2); + } else { + perror("mount"); + } + goto bail; + } + + dprintf("Mounted %s on %s\n", pathname, path); + + goto done; + +bail: + if (mounted) { + if (data->flags & NFS_MOUNT_VER3) + umount_v3(rem_path, clnt); + else + umount_v2(rem_path, clnt); + } + + ret = -1; + +done: + if (clnt) + client_free(clnt); + + if (sock != -1) + close(sock); + + return ret; +} diff --git a/usr/kinit/nfsmount/nfsmount.h b/usr/kinit/nfsmount/nfsmount.h new file mode 100644 index 0000000..7b28ded --- /dev/null +++ b/usr/kinit/nfsmount/nfsmount.h @@ -0,0 +1,34 @@ +#ifndef NFSMOUNT_NFSMOUNT_H +#define NFSMOUNT_NFSMOUNT_H + +#include <linux/nfs_mount.h> + +extern int nfs_port; + +extern int nfsmount_main(int argc, char *argv[]); +int nfs_mount(const char *rem_name, const char *hostname, + uint32_t server, const char *rem_path, + const char *path, struct nfs_mount_data *data); + +enum nfs_proto { + v2 = 2, + v3, +}; + +/* masked with NFS_MOUNT_FLAGMASK before mount() call */ +#define NFS_MOUNT_KLIBC_RONLY 0x00010000U + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +#ifndef MNTPROC_MNT +#define MNTPROC_MNT 1 +#endif +#ifndef MNTPROC_UMNT +#define MNTPROC_UMNT 3 +#endif + +#endif /* NFSMOUNT_NFSMOUNT_H */ diff --git a/usr/kinit/nfsmount/portmap.c b/usr/kinit/nfsmount/portmap.c new file mode 100644 index 0000000..0a3e2d0 --- /dev/null +++ b/usr/kinit/nfsmount/portmap.c @@ -0,0 +1,73 @@ +#include <sys/types.h> +#include <netinet/in.h> +#include <asm/byteorder.h> /* __constant_hton* */ +#include <stdio.h> +#include <stdlib.h> + +#include "nfsmount.h" +#include "sunrpc.h" + +struct portmap_call { + struct rpc_call rpc; + uint32_t program; + uint32_t version; + uint32_t proto; + uint32_t port; +}; + +struct portmap_reply { + struct rpc_reply rpc; + uint32_t port; +}; + +static struct portmap_call call = { + .rpc = { + .program = __constant_htonl(RPC_PMAP_PROGRAM), + .prog_vers = __constant_htonl(RPC_PMAP_VERSION), + .proc = __constant_htonl(PMAP_PROC_GETPORT), + } +}; + +uint32_t portmap(uint32_t server, uint32_t program, uint32_t version, uint32_t proto) +{ + struct portmap_reply reply; + struct client *clnt; + struct rpc rpc; + uint32_t port = 0; + + clnt = tcp_client(server, RPC_PMAP_PORT, 0); + if (clnt == NULL) { + clnt = udp_client(server, RPC_PMAP_PORT, 0); + if (clnt == NULL) + goto bail; + } + + call.program = htonl(program); + call.version = htonl(version); + call.proto = htonl(proto); + + rpc.call = (struct rpc_call *)&call; + rpc.call_len = sizeof(call); + rpc.reply = (struct rpc_reply *)&reply; + rpc.reply_len = sizeof(reply); + + if (rpc_call(clnt, &rpc) < 0) + goto bail; + + if (rpc.reply_len < sizeof(reply)) { + fprintf(stderr, "incomplete reply: %zu < %zu\n", + rpc.reply_len, sizeof(reply)); + goto bail; + } + + port = ntohl(reply.port); + +bail: + dprintf("Port for %d/%d[%s]: %d\n", program, version, + proto == IPPROTO_TCP ? "tcp" : "udp", port); + + if (clnt) + client_free(clnt); + + return port; +} diff --git a/usr/kinit/nfsmount/sunrpc.c b/usr/kinit/nfsmount/sunrpc.c new file mode 100644 index 0000000..0a7fcf5 --- /dev/null +++ b/usr/kinit/nfsmount/sunrpc.c @@ -0,0 +1,252 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <poll.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "nfsmount.h" +#include "sunrpc.h" + +/* + * The magic offset is needed here because RPC over TCP includes a + * field that RPC over UDP doesn't. Luvverly. + */ +static int rpc_do_reply(struct client *clnt, struct rpc *rpc, size_t off) +{ + int ret; + + if ((ret = read(clnt->sock, + ((char *)rpc->reply) + off, + rpc->reply_len - off)) == -1) { + perror("read"); + goto bail; + } else if (ret < sizeof(struct rpc_reply) - off) { + fprintf(stderr, "short read: %d < %zu\n", ret, + sizeof(struct rpc_reply) - off); + goto bail; + } + rpc->reply_len = ret + off; + + if ((!off && !(ntohl(rpc->reply->hdr.frag_hdr) & LAST_FRAG)) || + rpc->reply->hdr.udp.xid != rpc->call->hdr.udp.xid || + rpc->reply->hdr.udp.msg_type != htonl(RPC_REPLY)) { + fprintf(stderr, "bad reply\n"); + goto bail; + } + + if (ntohl(rpc->reply->state) != REPLY_OK) { + fprintf(stderr, "rpc failed: %d\n", ntohl(rpc->reply->state)); + goto bail; + } + + ret = 0; + goto done; + +bail: + ret = -1; +done: + return ret; +} + +static void rpc_header(struct client *clnt, struct rpc *rpc) +{ + (void)clnt; + + rpc->call->hdr.frag_hdr = htonl(LAST_FRAG | (rpc->call_len - 4)); + rpc->call->hdr.udp.xid = lrand48(); + rpc->call->hdr.udp.msg_type = htonl(RPC_CALL); + rpc->call->rpc_vers = htonl(2); +} + +static int rpc_call_tcp(struct client *clnt, struct rpc *rpc) +{ + int ret; + + rpc_header(clnt, rpc); + + if ((ret = write(clnt->sock, rpc->call, rpc->call_len)) == -1) { + perror("write"); + goto bail; + } else if (ret < rpc->call_len) { + fprintf(stderr, "short write: %d < %zu\n", ret, rpc->call_len); + goto bail; + } + + ret = rpc_do_reply(clnt, rpc, 0); + goto done; + + bail: + ret = -1; + + done: + return ret; +} + +static int rpc_call_udp(struct client *clnt, struct rpc *rpc) +{ +#define NR_FDS 1 +#define TIMEOUT_MS 3000 +#define MAX_TRIES 100 +#define UDP_HDR_OFF (sizeof(struct rpc_header) - sizeof(struct rpc_udp_header)) + struct pollfd fds[NR_FDS]; + int ret = -1; + int i; + + rpc_header(clnt, rpc); + + fds[0].fd = clnt->sock; + fds[0].events = POLLRDNORM; + + rpc->call_len -= UDP_HDR_OFF; + + for (i = 0; i < MAX_TRIES; i++) { + int timeout_ms = TIMEOUT_MS + (lrand48() % (TIMEOUT_MS / 2)); + if ((ret = write(clnt->sock, + ((char *)rpc->call) + UDP_HDR_OFF, + rpc->call_len)) == -1) { + perror("write"); + goto bail; + } else if (ret < rpc->call_len) { + fprintf(stderr, "short write: %d < %zu\n", ret, + rpc->call_len); + goto bail; + } + for (; i < MAX_TRIES; i++) { + if ((ret = poll(fds, NR_FDS, timeout_ms)) == -1) { + perror("poll"); + goto bail; + } + if (ret == 0) { + dprintf("Timeout #%d\n", i + 1); + break; + } + if ((ret = rpc_do_reply(clnt, rpc, UDP_HDR_OFF)) == 0) { + goto done; + } else { + dprintf("Failed on try #%d - retrying\n", + i + 1); + } + } + } + + bail: + ret = -1; + + done: + return ret; +} + +struct client *tcp_client(uint32_t server, uint16_t port, uint32_t flags) +{ + struct client *clnt = malloc(sizeof(*clnt)); + struct sockaddr_in addr; + int sock; + + if (clnt == NULL) { + perror("malloc"); + goto bail; + } + + memset(clnt, 0, sizeof(*clnt)); + + if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + perror("socket"); + goto bail; + } + + if ((flags & CLI_RESVPORT) && bindresvport(sock, 0) == -1) { + perror("bindresvport"); + goto bail; + } + + clnt->sock = sock; + clnt->call_stub = rpc_call_tcp; + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = server; + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("connect"); + goto bail; + } + + goto done; + bail: + if (clnt) { + free(clnt); + clnt = NULL; + } + done: + return clnt; +} + +struct client *udp_client(uint32_t server, uint16_t port, uint32_t flags) +{ + struct client *clnt = malloc(sizeof(*clnt)); + struct sockaddr_in addr; + int sock; + + if (clnt == NULL) { + perror("malloc"); + goto bail; + } + + memset(clnt, 0, sizeof(*clnt)); + + if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + perror("socket"); + goto bail; + } + + if ((flags & CLI_RESVPORT) && bindresvport(sock, 0) == -1) { + perror("bindresvport"); + goto bail; + } else { + struct sockaddr_in me; + + me.sin_family = AF_INET; + me.sin_port = 0; + me.sin_addr.s_addr = INADDR_ANY; + + if (0 && bind(sock, (struct sockaddr *)&me, sizeof(me)) == -1) { + perror("bind"); + goto bail; + } + } + + clnt->sock = sock; + clnt->call_stub = rpc_call_udp; + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = server; + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("connect"); + goto bail; + } + + goto done; + bail: + if (clnt) { + free(clnt); + clnt = NULL; + } + done: + return clnt; +} + +void client_free(struct client *c) +{ + if (c->sock != -1) + close(c->sock); + free(c); +} + +int rpc_call(struct client *client, struct rpc *rpc) +{ + return client->call_stub(client, rpc); +} diff --git a/usr/kinit/nfsmount/sunrpc.h b/usr/kinit/nfsmount/sunrpc.h new file mode 100644 index 0000000..1bcfeea --- /dev/null +++ b/usr/kinit/nfsmount/sunrpc.h @@ -0,0 +1,110 @@ +/* + * open-coded SunRPC structures + */ +#ifndef NFSMOUNT_SUNRPC_H +#define NFSMOUNT_SUNRPC_H + +#include <sys/types.h> +#include <inttypes.h> + +#define SUNRPC_PORT 111 +#define MOUNT_PORT 627 + +#define RPC_CALL 0 +#define RPC_REPLY 1 + +#define PORTMAP_PROGRAM 100000 +#define NLM_PROGRAM 100021 + +#define RPC_PMAP_PROGRAM 100000 +#define RPC_PMAP_VERSION 2 +#define RPC_PMAP_PORT 111 + +#define PMAP_PROC_NULL 0 +#define PMAP_PROC_SET 1 +#define PMAP_PROC_UNSET 2 +#define PMAP_PROC_GETPORT 3 +#define PMAP_PROC_DUMP 4 + +#define LAST_FRAG 0x80000000 + +#define REPLY_OK 0 +#define REPLY_DENIED 1 + +#define SUCCESS 0 +#define PROG_UNAVAIL 1 +#define PROG_MISMATCH 2 +#define PROC_UNAVAIL 3 +#define GARBAGE_ARGS 4 +#define SYSTEM_ERR 5 + +enum { + AUTH_NULL, + AUTH_UNIX, +}; + +struct rpc_auth { + uint32_t flavor; + uint32_t len; + uint32_t body[]; +}; + +struct rpc_udp_header { + uint32_t xid; + uint32_t msg_type; +}; + +struct rpc_header { + uint32_t frag_hdr; + struct rpc_udp_header udp; +}; + +struct rpc_call { + struct rpc_header hdr; + uint32_t rpc_vers; + + uint32_t program; + uint32_t prog_vers; + uint32_t proc; + uint32_t cred_flavor; + + uint32_t cred_len; + uint32_t vrf_flavor; + uint32_t vrf_len; +}; + +struct rpc_reply { + struct rpc_header hdr; + uint32_t reply_state; + uint32_t vrf_flavor; + uint32_t vrf_len; + uint32_t state; +}; + +struct rpc { + struct rpc_call *call; + size_t call_len; + struct rpc_reply *reply; + size_t reply_len; +}; + +struct client; + +typedef int (*call_stub) (struct client *, struct rpc *); + +struct client { + int sock; + call_stub call_stub; +}; + +#define CLI_RESVPORT 00000001 + +struct client *tcp_client(uint32_t server, uint16_t port, uint32_t flags); +struct client *udp_client(uint32_t server, uint16_t port, uint32_t flags); +void client_free(struct client *client); + +int rpc_call(struct client *client, struct rpc *rpc); + +uint32_t portmap(uint32_t server, uint32_t program, uint32_t version, uint32_t proto); + +#endif /* NFSMOUNT_SUNRPC_H */ diff --git a/usr/kinit/nfsroot.c b/usr/kinit/nfsroot.c new file mode 100644 index 0000000..3b80773 --- /dev/null +++ b/usr/kinit/nfsroot.c @@ -0,0 +1,111 @@ +#include <arpa/inet.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "kinit.h" +#include "netdev.h" +#include "nfsmount.h" + +static char *sub_client(__u32 client, char *path, size_t len) +{ + struct in_addr addr = { client }; + char buf[len]; + + if (strstr(path, "%s") != NULL) { + if (client == INADDR_NONE) { + fprintf(stderr, "Root-NFS: no client address\n"); + exit(1); + } + + snprintf(buf, len, path, inet_ntoa(addr)); + strcpy(path, buf); + } + + return path; +} + +#define NFS_ARGC 6 +#define MOUNT_POINT "/root" + +int mount_nfs_root(int argc, char *argv[], int flags) +{ + (void)flags; /* FIXME - don't ignore this */ + + struct in_addr addr = { INADDR_NONE }; + __u32 client = INADDR_NONE; + const int len = 1024; + struct netdev *dev; + char *mtpt = MOUNT_POINT; + char *path = NULL; + char *dev_bootpath = NULL; + char root[len]; + char *x, *opts; + int ret = 0; + int a = 1; + char *nfs_argv[NFS_ARGC + 1] = { "NFS-Mount" }; + + for (dev = ifaces; dev; dev = dev->next) { + if (dev->ip_server != INADDR_NONE && + dev->ip_server != INADDR_ANY) { + addr.s_addr = dev->ip_server; + client = dev->ip_addr; + dev_bootpath = dev->bootpath; + break; + } + if (dev->ip_addr != INADDR_NONE && dev->ip_addr != INADDR_ANY) + client = dev->ip_addr; + } + + /* + * if the "nfsroot" option is set then it overrides + * bootpath supplied by the boot server. + */ + if ((path = get_arg(argc, argv, "nfsroot=")) == NULL) { + if ((path = dev_bootpath) == NULL || path[0] == '\0') + /* no path - set a default */ + path = (char *)"/tftpboot/%s"; + } else if (dev_bootpath && dev_bootpath[0] != '\0') + fprintf(stderr, + "nfsroot=%s overrides boot server bootpath %s\n", + path, dev_bootpath); + + if ((opts = strchr(path, ',')) != NULL) { + *opts++ = '\0'; + nfs_argv[a++] = (char *)"-o"; + nfs_argv[a++] = opts; + } + + if ((x = strchr(path, ':')) == NULL) { + if (addr.s_addr == INADDR_NONE) { + fprintf(stderr, "Root-NFS: no server defined\n"); + exit(1); + } + + snprintf(root, len, "%s:%s", inet_ntoa(addr), path); + } else { + strcpy(root, path); + } + + nfs_argv[a++] = sub_client(client, root, len); + + dprintf("NFS-Root: mounting %s on %s with options \"%s\"\n", + nfs_argv[a-1], mtpt, opts ? opts : ""); + + nfs_argv[a++] = mtpt; + nfs_argv[a] = NULL; + assert(a <= NFS_ARGC); + + dump_args(a, nfs_argv); + + if ((ret = nfsmount_main(a, nfs_argv)) != 0) { + ret = -1; + goto done; + } + +done: + return ret; +} diff --git a/usr/kinit/ramdisk_load.c b/usr/kinit/ramdisk_load.c new file mode 100644 index 0000000..e3e15d8 --- /dev/null +++ b/usr/kinit/ramdisk_load.c @@ -0,0 +1,281 @@ +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <linux/fs.h> +#include <linux/cdrom.h> +#include <linux/fd.h> + +#include "kinit.h" +#include "do_mounts.h" +#include "fstype.h" +#include "zlib.h" + +#define BUF_SZ 65536 + +static void wait_for_key(void) +{ + /* Wait until the user presses Enter */ + while (getchar() != '\n') + ; +} + +static int change_disk(const char *devpath, int rfd, int disk) +{ + /* Try to eject and/or quiesce the device */ + sync(); + if (ioctl(rfd, FDEJECT, 0)) { + if (errno == ENOTTY) { + /* Not a floppy */ + ioctl(rfd, CDROMEJECT, 0); + } else { + /* Non-ejectable floppy */ + ioctl(rfd, FDRESET, (void *)FD_RESET_IF_NEEDED); + } + } + close(rfd); + + fprintf(stderr, + "\nPlease insert disk %d for ramdisk and press Enter...", disk); + wait_for_key(); + + return open(devpath, O_RDONLY); +} + +#ifdef CONFIG_KLIBC_ZLIB +/* Also used in initrd.c */ +int load_ramdisk_compressed(const char *devpath, FILE * wfd, + off_t ramdisk_start) +{ + int rfd = -1; + unsigned long long ramdisk_size, ramdisk_left; + int disk = 1; + ssize_t bytes; + int rv; + unsigned char in_buf[BUF_SZ], out_buf[BUF_SZ]; + z_stream zs; + + zs.zalloc = Z_NULL; /* Use malloc() */ + zs.zfree = Z_NULL; /* Use free() */ + zs.next_in = Z_NULL; /* No data read yet */ + zs.avail_in = 0; + zs.next_out = out_buf; + zs.avail_out = BUF_SZ; + + if (inflateInit2(&zs, 32 + 15) != Z_OK) + goto err1; + + rfd = open(devpath, O_RDONLY); + if (rfd < 0) + goto err2; + + /* Set to the size of the medium, or "infinite" */ + if (ioctl(rfd, BLKGETSIZE64, &ramdisk_size)) + ramdisk_size = ~0ULL; + + do { + /* Purge the output preferentially over reading new + input, so we don't end up overrunning the input by + accident and demanding a new disk which doesn't + exist... */ + if (zs.avail_out == 0) { + _fwrite(out_buf, BUF_SZ, wfd); + zs.next_out = out_buf; + zs.avail_out = BUF_SZ; + } else if (zs.avail_in == 0) { + if (ramdisk_start >= ramdisk_size) { + rfd = change_disk(devpath, rfd, ++disk); + if (rfd < 0) + goto err2; + + if (ioctl(rfd, BLKGETSIZE64, &ramdisk_size)) + ramdisk_size = ~0ULL; + ramdisk_start = 0; + dprintf("New size = %llu\n", ramdisk_size); + } + do { + ramdisk_left = ramdisk_size - ramdisk_start; + bytes = min(ramdisk_left, + (unsigned long long)BUF_SZ); + bytes = pread(rfd, in_buf, bytes, + ramdisk_start); + } while (bytes == -1 && errno == EINTR); + if (bytes <= 0) + goto err2; + ramdisk_start += bytes; + zs.next_in = in_buf; + zs.avail_in = bytes; + + /* Print dots if we're reading from a real block device */ + if (ramdisk_size != ~0ULL) + putc('.', stderr); + } + rv = inflate(&zs, Z_SYNC_FLUSH); + } while (rv == Z_OK || rv == Z_BUF_ERROR); + + dprintf("kinit: inflate returned %d\n", rv); + + if (rv != Z_STREAM_END) + goto err2; + + /* Write the last */ + _fwrite(out_buf, BUF_SZ - zs.avail_out, wfd); + dprintf("kinit: writing %d bytes\n", BUF_SZ - zs.avail_out); + + inflateEnd(&zs); + return 0; + +err2: + inflateEnd(&zs); +err1: + return -1; +} +#else +int load_ramdisk_compressed(const char *devpath, FILE * wfd, + off_t ramdisk_start) +{ + fprintf(stderr, "Compressed ramdisk not supported\n"); + return -1; +} +#endif + +static int +load_ramdisk_raw(const char *devpath, FILE * wfd, off_t ramdisk_start, + unsigned long long fssize) +{ + unsigned long long ramdisk_size, ramdisk_left; + int disk = 1; + ssize_t bytes; + unsigned char buf[BUF_SZ]; + int rfd; + + rfd = open(devpath, O_RDONLY); + if (rfd < 0) + return -1; + + /* Set to the size of the medium, or "infinite" */ + if (ioctl(rfd, BLKGETSIZE64, &ramdisk_size)) + ramdisk_size = ~0ULL; + + dprintf("start: %llu size: %llu fssize: %llu\n", + ramdisk_start, ramdisk_size, fssize); + + while (fssize) { + + if (ramdisk_start >= ramdisk_size) { + rfd = change_disk(devpath, rfd, ++disk); + if (rfd < 0) + return -1; + + if (ioctl(rfd, BLKGETSIZE64, &ramdisk_size)) + ramdisk_size = ~0ULL; + ramdisk_start = 0; + } + + do { + ramdisk_left = + min(ramdisk_size - ramdisk_start, fssize); + bytes = min(ramdisk_left, (unsigned long long)BUF_SZ); + bytes = pread(rfd, buf, bytes, ramdisk_start); + } while (bytes == -1 && errno == EINTR); + if (bytes <= 0) + break; + _fwrite(buf, bytes, wfd); + + ramdisk_start += bytes; + fssize -= bytes; + + /* Print dots if we're reading from a real block device */ + if (ramdisk_size != ~0ULL) + putc('.', stderr); + } + + return !!fssize; +} + +int ramdisk_load(int argc, char *argv[]) +{ + const char *arg_prompt_ramdisk = get_arg(argc, argv, "prompt_ramdisk="); + const char *arg_ramdisk_blocksize = + get_arg(argc, argv, "ramdisk_blocksize="); + const char *arg_ramdisk_start = get_arg(argc, argv, "ramdisk_start="); + const char *arg_ramdisk_device = get_arg(argc, argv, "ramdisk_device="); + + int prompt_ramdisk = arg_prompt_ramdisk ? atoi(arg_prompt_ramdisk) : 0; + int ramdisk_blocksize = + arg_ramdisk_blocksize ? atoi(arg_ramdisk_blocksize) : 512; + off_t ramdisk_start = + arg_ramdisk_start + ? strtoumax(arg_ramdisk_start, NULL, 10) * ramdisk_blocksize : 0; + const char *ramdisk_device = + arg_ramdisk_device ? arg_ramdisk_device : "/dev/fd0"; + + dev_t ramdisk_dev; + int rfd; + FILE *wfd; + const char *fstype; + unsigned long long fssize; + int is_gzip = 0; + int err; + + if (prompt_ramdisk) { + fprintf(stderr, + "Please insert disk for ramdisk and press Enter..."); + wait_for_key(); + } + + ramdisk_dev = name_to_dev_t(ramdisk_device); + if (!ramdisk_dev) { + fprintf(stderr, + "Failure loading ramdisk: unknown device: %s\n", + ramdisk_device); + return 0; + } + + create_dev("/dev/rddev", ramdisk_dev); + create_dev("/dev/ram0", Root_RAM0); + rfd = open("/dev/rddev", O_RDONLY); + wfd = fopen("/dev/ram0", "w"); + + if (rfd < 0 || !wfd) { + perror("Could not open ramdisk device"); + return 0; + } + + /* Check filesystem type */ + if (identify_fs(rfd, &fstype, &fssize, ramdisk_start) || + (fssize == 0 && !(is_gzip = !strcmp(fstype, "gzip")))) { + fprintf(stderr, + "Failure loading ramdisk: unknown filesystem type\n"); + close(rfd); + fclose(wfd); + return 0; + } + + dprintf("kinit: ramdisk is %s, size %llu\n", fstype, fssize); + + fprintf(stderr, "Loading ramdisk (%s) ...", is_gzip ? "gzip" : "raw"); + + close(rfd); + + if (is_gzip) + err = load_ramdisk_compressed("/dev/rddev", wfd, ramdisk_start); + else + err = load_ramdisk_raw("/dev/rddev", wfd, + ramdisk_start, fssize); + + fclose(wfd); + + putc('\n', stderr); + + if (err) { + perror("Failure loading ramdisk"); + return 0; + } + + return 1; +} diff --git a/usr/kinit/readfile.c b/usr/kinit/readfile.c new file mode 100644 index 0000000..7a16b4a --- /dev/null +++ b/usr/kinit/readfile.c @@ -0,0 +1,86 @@ +/* + * Read the entire contents of a file into malloc'd storage. This + * is mostly useful for things like /proc files where we can't just + * fstat() to get the length and then mmap(). + * + * Returns the number of bytes read, or -1 on error. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/stat.h> + +#include "kinit.h" + +ssize_t freadfile(FILE *f, char **pp) +{ + size_t bs; /* Decent starting point... */ + size_t bf; /* Bytes free */ + size_t bu = 0; /* Bytes used */ + char *buffer, *nb; + size_t rv; + int old_errno = errno; + + bs = BUFSIZ; /* A guess as good as any */ + bf = bs; + buffer = malloc(bs); + + if (!buffer) + return -1; + + for (;;) { + errno = 0; + + while (bf && (rv = _fread(buffer + bu, bf, f))) { + bu += rv; + bf -= rv; + } + + if (errno && errno != EINTR && errno != EAGAIN) { + /* error */ + free(buffer); + return -1; + } + + if (bf) { + /* Hit EOF, no error */ + + /* Try to free superfluous memory */ + if ((nb = realloc(buffer, bu + 1))) + buffer = nb; + + /* Null-terminate result for good measure */ + buffer[bu] = '\0'; + + *pp = buffer; + errno = old_errno; + return bu; + } + + /* Double the size of the buffer */ + bf += bs; + bs += bs; + if (!(nb = realloc(buffer, bs))) { + /* out of memory error */ + free(buffer); + return -1; + } + buffer = nb; + } +} + +ssize_t readfile(const char *filename, char **pp) +{ + FILE *f = fopen(filename, "r"); + ssize_t rv; + + if (!f) + return -1; + + rv = freadfile(f, pp); + + fclose(f); + + return rv; +} diff --git a/usr/kinit/resume/Kbuild b/usr/kinit/resume/Kbuild new file mode 100644 index 0000000..c804a85 --- /dev/null +++ b/usr/kinit/resume/Kbuild @@ -0,0 +1,34 @@ +# +# Kbuild file for resume +# + +static-y := static/resume +shared-y := shared/resume + +# common .o files +objs := resume.o resumelib.o + +# TODO - do we want a stripped version +# TODO - do we want the static.g + shared.g directories? + +# Create lib.a with all object files (used by kinit) +lib-y := $(objs) + +# Additional include paths files +KLIBCCFLAGS += -I$(srctree)/$(src)/.. + +# .o files used to built executables +static/resume-y := $(objs) +static/resume-lib := ../lib.a +shared/resume-y := $(objs) +shared/resume-lib := ../lib.a + +# Cleaning +clean-dirs := static shared + +# install binary +ifdef KLIBCSHAREDFLAGS +install-y := $(shared-y) +else +install-y := $(static-y) +endif diff --git a/usr/kinit/resume/resume.c b/usr/kinit/resume/resume.c new file mode 100644 index 0000000..2138078 --- /dev/null +++ b/usr/kinit/resume/resume.c @@ -0,0 +1,25 @@ +/* + * Handle resume from suspend-to-disk + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "resume.h" + +char *progname; + +static __noreturn usage(void) +{ + fprintf(stderr, "Usage: %s /dev/<resumedevice> [offset]\n", progname); + exit(1); +} + +int main(int argc, char *argv[]) +{ + progname = argv[0]; + if (argc < 2 || argc > 3) + usage(); + + return resume(argv[1], (argc > 2) ? strtoull(argv[2], NULL, 0) : 0ULL); +} diff --git a/usr/kinit/resume/resume.h b/usr/kinit/resume/resume.h new file mode 100644 index 0000000..5fb929f --- /dev/null +++ b/usr/kinit/resume/resume.h @@ -0,0 +1,7 @@ +#ifndef RESUME_H +#define RESUME_H + +int do_resume(int argc, char *argv[]); +int resume(const char *resume_file, unsigned long long resume_offset); + +#endif /* RESUME_H */ diff --git a/usr/kinit/resume/resumelib.c b/usr/kinit/resume/resumelib.c new file mode 100644 index 0000000..03e596a --- /dev/null +++ b/usr/kinit/resume/resumelib.c @@ -0,0 +1,106 @@ +/* + * Handle resume from suspend-to-disk + */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> + +#include "kinit.h" +#include "do_mounts.h" +#include "resume.h" + +#ifndef CONFIG_PM_STD_PARTITION +# define CONFIG_PM_STD_PARTITION "" +#endif + +int do_resume(int argc, char *argv[]) +{ + const char *resume_file = CONFIG_PM_STD_PARTITION; + const char *resume_arg; + unsigned long long resume_offset; + + resume_arg = get_arg(argc, argv, "resume="); + resume_file = resume_arg ? resume_arg : resume_file; + /* No resume device specified */ + if (!resume_file[0]) + return 0; + + resume_arg = get_arg(argc, argv, "resume_offset="); + resume_offset = resume_arg ? strtoull(resume_arg, NULL, 0) : 0ULL; + + /* Fix: we either should consider reverting the device back to + ordinary swap, or (better) put that code into swapon */ + /* Noresume requested */ + if (get_flag(argc, argv, "noresume")) + return 0; + return resume(resume_file, resume_offset); +} + +int resume(const char *resume_file, unsigned long long resume_offset) +{ + dev_t resume_device; + int attr_fd = -1; + char attr_value[64]; + int len; + + resume_device = name_to_dev_t(resume_file); + + if (major(resume_device) == 0) { + fprintf(stderr, "Invalid resume device: %s\n", resume_file); + goto failure; + } + + if ((attr_fd = open("/sys/power/resume_offset", O_WRONLY)) < 0) + goto fail_offset; + + len = snprintf(attr_value, sizeof attr_value, + "%llu", + resume_offset); + + /* This should never happen */ + if (len >= sizeof attr_value) + goto fail_offset; + + if (write(attr_fd, attr_value, len) != len) + goto fail_offset; + + close(attr_fd); + + if ((attr_fd = open("/sys/power/resume", O_WRONLY)) < 0) + goto fail_r; + + len = snprintf(attr_value, sizeof attr_value, + "%u:%u", + major(resume_device), minor(resume_device)); + + /* This should never happen */ + if (len >= sizeof attr_value) + goto fail_r; + + dprintf("kinit: trying to resume from %s\n", resume_file); + + if (write(attr_fd, attr_value, len) != len) + goto fail_r; + + /* Okay, what are we still doing alive... */ +failure: + if (attr_fd >= 0) + close(attr_fd); + dprintf("kinit: No resume image, doing normal boot...\n"); + return -1; + +fail_offset: + fprintf(stderr, "Cannot write /sys/power/resume_offset " + "(no software suspend kernel support, or old kernel version?)\n"); + goto failure; + +fail_r: + fprintf(stderr, "Cannot write /sys/power/resume " + "(no software suspend kernel support?)\n"); + goto failure; +} diff --git a/usr/kinit/run-init/Kbuild b/usr/kinit/run-init/Kbuild new file mode 100644 index 0000000..eeff906 --- /dev/null +++ b/usr/kinit/run-init/Kbuild @@ -0,0 +1,38 @@ +# +# Kbuild file for run-init +# + +static-y := static/run-init +shared-y := shared/run-init + +# common .o files +objs := run-init.o runinitlib.o + +# TODO - do we want a stripped version +# TODO - do we want the static.g + shared.g directories? + +# Create built-in.o with all object files (used by kinit) +lib-y := $(objs) + +# force run-init to not have an executable stack (to keep READ_IMPLIES_EXEC +# personality(2) flag from getting set and passed to init). +EXTRA_KLIBCLDFLAGS += -z noexecstack + +# Additional include paths files +KLIBCCFLAGS += -I$(srctree)/$(src)/.. + +# .o files used to built executables +static/run-init-y := $(objs) +static/run-init-lib := ../lib.a +shared/run-init-y := $(objs) +shared/run-init-lib := ../lib.a + +# Cleaning +clean-dirs := static shared + +# install binary +ifdef KLIBCSHAREDFLAGS +install-y := $(shared-y) +else +install-y := $(static-y) +endif diff --git a/usr/kinit/run-init/run-init.c b/usr/kinit/run-init/run-init.c new file mode 100644 index 0000000..6a4ad3e --- /dev/null +++ b/usr/kinit/run-init/run-init.c @@ -0,0 +1,114 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * Usage: exec run-init [-d caps] [-c /dev/console] [-n] [-p] /real-root /sbin/init "$@" + * + * This program should be called as the last thing in a shell script + * acting as /init in an initramfs; it does the following: + * + * 1. Delete all files in the initramfs; + * 2. Remounts /real-root onto the root filesystem; + * 3. Drops comma-separated list of capabilities; + * 4. Chroots; + * 5. Opens /dev/console; + * 6. Spawns the specified init program (with arguments.) + * + * With the -p option, it skips step 1 in order to allow the initramfs to + * be persisted into the running system. + * + * With the -n option, it skips steps 1, 2 and 6 and can be used to check + * whether the given root and init are likely to work. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include "run-init.h" + +static const char *program; + +static void __attribute__ ((noreturn)) usage(void) +{ + fprintf(stderr, + "Usage: exec %s [-d caps] [-c consoledev] [-n] [-p] /real-root /sbin/init [args]\n", + program); + exit(1); +} + +int main(int argc, char *argv[]) +{ + /* Command-line options and defaults */ + const char *console = "/dev/console"; + const char *realroot; + const char *init; + const char *error; + const char *drop_caps = NULL; + bool dry_run = false; + bool persist_initramfs = false; + char **initargs; + + /* Variables... */ + int o; + + /* Parse the command line */ + program = argv[0]; + + while ((o = getopt(argc, argv, "c:d:pn")) != -1) { + if (o == 'c') { + console = optarg; + } else if (o == 'd') { + drop_caps = optarg; + } else if (o == 'n') { + dry_run = true; + } else if (o == 'p') { + persist_initramfs = true; + } else { + usage(); + } + } + + if (argc - optind < 2) + usage(); + + realroot = argv[optind]; + init = argv[optind + 1]; + initargs = argv + optind + 1; + + error = run_init(realroot, console, drop_caps, dry_run, persist_initramfs, init, initargs); + + if (error) { + fprintf(stderr, "%s: %s: %s\n", program, error, strerror(errno)); + return 1; + } else { + /* Must have been a dry run */ + return 0; + } +} diff --git a/usr/kinit/run-init/run-init.h b/usr/kinit/run-init/run-init.h new file mode 100644 index 0000000..5240ce7 --- /dev/null +++ b/usr/kinit/run-init/run-init.h @@ -0,0 +1,38 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef RUN_INIT_H +#define RUN_INIT_H + +#include <stdbool.h> + +const char *run_init(const char *realroot, const char *console, + const char *drop_caps, bool dry_run, + bool persist_initramfs, const char *init, + char **initargs); + +#endif diff --git a/usr/kinit/run-init/runinitlib.c b/usr/kinit/run-init/runinitlib.c new file mode 100644 index 0000000..1c2e56a --- /dev/null +++ b/usr/kinit/run-init/runinitlib.c @@ -0,0 +1,232 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * run_init(realroot, consoledev, drop_caps, persist_initramfs, init, initargs) + * + * This function should be called as the last thing in kinit, + * from initramfs, it does the following: + * + * - Delete all files in the initramfs; + * - Remounts /real-root onto the root filesystem; + * - Chroots; + * - Drops comma-separated list of capabilities; + * - Opens /dev/console; + * - Spawns the specified init program (with arguments.) + * + * On failure, returns a human-readable error message. + */ + +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/vfs.h> +#include "run-init.h" +#include "capabilities.h" + +/* Make it possible to compile on glibc by including constants that the + always-behind shipped glibc headers may not include. Classic example + on why the lack of ABI headers screw us up. */ +#ifndef TMPFS_MAGIC +# define TMPFS_MAGIC 0x01021994 +#endif +#ifndef RAMFS_MAGIC +# define RAMFS_MAGIC 0x858458f6 +#endif +#ifndef MS_MOVE +# define MS_MOVE 8192 +#endif + +static int nuke(const char *what); + +static int nuke_dirent(int len, const char *dir, const char *name, dev_t me) +{ + int bytes = len + strlen(name) + 2; + char path[bytes]; + int xlen; + struct stat st; + + xlen = snprintf(path, bytes, "%s/%s", dir, name); + assert(xlen < bytes); + + if (lstat(path, &st)) + return ENOENT; /* Return 0 since already gone? */ + + if (st.st_dev != me) + return 0; /* DO NOT recurse down mount points!!!!! */ + + return nuke(path); +} + +/* Wipe the contents of a directory, but not the directory itself */ +static int nuke_dir(const char *what) +{ + int len = strlen(what); + DIR *dir; + struct dirent *d; + int err = 0; + struct stat st; + + if (lstat(what, &st)) + return errno; + + if (!S_ISDIR(st.st_mode)) + return ENOTDIR; + + if (!(dir = opendir(what))) { + /* EACCES means we can't read it. Might be empty and removable; + if not, the rmdir() in nuke() will trigger an error. */ + return (errno == EACCES) ? 0 : errno; + } + + while ((d = readdir(dir))) { + /* Skip . and .. */ + if (d->d_name[0] == '.' && + (d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; + + err = nuke_dirent(len, what, d->d_name, st.st_dev); + if (err) { + closedir(dir); + return err; + } + } + + closedir(dir); + + return 0; +} + +static int nuke(const char *what) +{ + int rv; + int err = 0; + + rv = unlink(what); + if (rv < 0) { + if (errno == EISDIR) { + /* It's a directory. */ + err = nuke_dir(what); + if (!err) + err = rmdir(what) ? errno : err; + } else { + err = errno; + } + } + + if (err) { + errno = err; + return err; + } else { + return 0; + } +} + +const char *run_init(const char *realroot, const char *console, + const char *drop_caps, bool dry_run, + bool persist_initramfs, const char *init, char **initargs) +{ + struct stat rst, cst, ist; + struct statfs sfs; + int confd; + + /* First, change to the new root directory */ + if (chdir(realroot)) + return "chdir to new root"; + + /* This is a potentially highly destructive program. Take some + extra precautions. */ + + /* Make sure the current directory is not on the same filesystem + as the root directory */ + if (stat("/", &rst) || stat(".", &cst)) + return "stat"; + + if (rst.st_dev == cst.st_dev) + return "current directory on the same filesystem as the root"; + + /* Make sure we're on a ramfs */ + if (statfs("/", &sfs)) + return "statfs /"; + if (sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC) + return "rootfs not a ramfs or tmpfs"; + + /* Okay, I think we should be safe... */ + + if (!dry_run) { + if (!persist_initramfs) { + /* Delete rootfs contents */ + if (nuke_dir("/")) + return "nuking initramfs contents"; + } + + /* Overmount the root */ + if (mount(".", "/", NULL, MS_MOVE, NULL)) + return "overmounting root"; + } + + /* chroot, chdir */ + if (chroot(".") || chdir("/")) + return "chroot"; + + /* Drop capabilities */ + if (drop_capabilities(drop_caps) < 0) + return "dropping capabilities"; + + /* Open /dev/console */ + if ((confd = open(console, O_RDWR)) < 0) + return "opening console"; + if (!dry_run) { + dup2(confd, 0); + dup2(confd, 1); + dup2(confd, 2); + } + close(confd); + + if (!dry_run) { + /* Spawn init */ + execv(init, initargs); + return init; /* Failed to spawn init */ + } else { + if (stat(init, &ist)) + return init; + if (!S_ISREG(ist.st_mode) || !(ist.st_mode & S_IXUGO)) { + errno = EACCES; + return init; + } + return NULL; /* Success */ + } +} diff --git a/usr/kinit/xpio.c b/usr/kinit/xpio.c new file mode 100644 index 0000000..42a9844 --- /dev/null +++ b/usr/kinit/xpio.c @@ -0,0 +1,51 @@ +/* + * Looping versions of pread() and pwrite() + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include "xpio.h" + +ssize_t xpread(int fd, void *buf, size_t count, off_t offset) +{ + ssize_t ctr = 0; + ssize_t rv = 0; + char *bp = buf; + + while (count) { + rv = pread(fd, bp, count, offset); + + if (rv == 0 || (rv == -1 && errno != EINTR)) + break; + + bp += rv; + count -= rv; + offset += rv; + ctr += rv; + } + + return ctr ? ctr : rv; +} + +ssize_t xpwrite(int fd, void *buf, size_t count, off_t offset) +{ + ssize_t ctr = 0; + ssize_t rv = 0; + char *bp = buf; + + while (count) { + rv = pwrite(fd, bp, count, offset); + + if (rv == 0 || (rv == -1 && errno != EINTR)) + break; + + bp += rv; + count -= rv; + offset += rv; + ctr += rv; + } + + return ctr ? ctr : rv; +} diff --git a/usr/kinit/xpio.h b/usr/kinit/xpio.h new file mode 100644 index 0000000..0596a32 --- /dev/null +++ b/usr/kinit/xpio.h @@ -0,0 +1,11 @@ +/* + * kinit/xpio.h + */ + +#ifndef KINIT_XPIO_H +#define KINIT_XPIO_H + +ssize_t xpread(int fd, void *buf, size_t count, off_t offset); +ssize_t xpwrite(int fd, void *buf, size_t count, off_t offset); + +#endif /* KINIT_XPIO_H */ diff --git a/usr/klibc/.gitignore b/usr/klibc/.gitignore new file mode 100644 index 0000000..5f4a9af --- /dev/null +++ b/usr/klibc/.gitignore @@ -0,0 +1,12 @@ +errlist.c +.shared-stub.o.d +klibc*.so +klib.list +.klib.list.cmd +libc.a +libc.so +libc.so.hash +sha1hash +tests/* +!tests/Kbuild +!tests/*.c diff --git a/usr/klibc/CAVEATS b/usr/klibc/CAVEATS new file mode 100644 index 0000000..39949c2 --- /dev/null +++ b/usr/klibc/CAVEATS @@ -0,0 +1,49 @@ + ------------------------------------------------- + Please note the following caveats when using klibc: + ------------------------------------------------- + +optimization: +------------- +Compiling with -O0 is not supported. It may or may not work; please +use -O1 if you want to do maximize debuggability. + +Compiling with -O0 is more likely to work on gcc 3. + + +setjmp()/longjmp(): +------------------- +setjmp() and longjmp() *do not* save signal state. sigsetjmp() and +siglongjmp() *do* save the signal mask if the extra argument is nonzero. + +The standards actually state that if you pass longjmp() a final value +of zero the library should change that to a 1! Presumably the reason +is so people who write broken code can get away with writing +longjmp(buf); or something equally bad. If you pass longjmp() a final +value of 0 you get what you deserve -- setjmp() will happily return 0. + + +stdio: +------ +Only a small subset of the stdio functions are implemented. Those +that are implemented do not buffer, although they *do* trap EINTR or +short read/writes and iterate. + +_fread() and _fwrite(), which take only one size argument (like +read/write), but do handle EINTR/short return are also available. + + +namespaces: +----------- +klibc frequently includes headers in other headers in a way that +exposes more symbols than POSIX says they should. "Live with it." + + +theading: +--------- +klibc is not thread-safe. Consequently, clone() or any of the +pthreads functions are not included. + + +bsd_signal vs sysv_signal: +-------------------------- +signal() now defaults to bsd_signal(). diff --git a/usr/klibc/Kbuild b/usr/klibc/Kbuild new file mode 100644 index 0000000..8b13219 --- /dev/null +++ b/usr/klibc/Kbuild @@ -0,0 +1,219 @@ +# +# Kbuild file for klibc +# + +# Tell that we are building klibc +export klibc-build := y + +# Generate syscall stubs +klib-y += syscalls/ +# Generate socket calls stubs +klib-y += socketcalls/ +# zlib, if configured +klib-$(CONFIG_KLIBC_ZLIB) += zlib/ +# arch specific .o files +klib-y += arch/$(KLIBCARCHDIR)/ + +klib-y += vsnprintf.o snprintf.o vsprintf.o sprintf.o \ + asprintf.o vasprintf.o \ + vsscanf.o sscanf.o ctypes.o \ + strntoumax.o strntoimax.o \ + atoi.o atol.o atoll.o \ + strtol.o strtoll.o strtoul.o strtoull.o \ + strtoimax.o strtoumax.o \ + globals.o exit.o atexit.o onexit.o \ + execl.o execle.o execv.o execvpe.o execvp.o execlp.o execlpe.o \ + fork.o vfork.o wait.o wait3.o waitpid.o system.o \ + setpgrp.o getpgrp.o daemon.o \ + printf.o vprintf.o fprintf.o vfprintf.o perror.o \ + statfs.o fstatfs.o umount.o \ + creat.o open.o openat.o \ + fread2.o fwrite2.o fgets.o fputc.o fputs.o puts.o putchar.o \ + clock_nanosleep.o nanosleep.o sleep.o usleep.o \ + strtotimespec.o strtotimeval.o \ + raise.o abort.o assert.o alarm.o pause.o \ + __signal.o sysv_signal.o bsd_signal.o siglist.o sigabbrev.o \ + siglongjmp.o \ + sigaction.o sigpending.o sigprocmask.o sigsuspend.o \ + pselect.o ppoll.o \ + pread.o pwrite.o \ + brk.o sbrk.o malloc.o realloc.o zalloc.o calloc.o \ + mmap.o shm_open.o shm_unlink.o \ + memcpy.o memcmp.o memset.o memccpy.o memmem.o memswap.o \ + memmove.o memchr.o memrchr.o bzero.o \ + strcasecmp.o strncasecmp.o strndup.o strerror.o strsignal.o \ + strcat.o strchr.o strcmp.o strcpy.o strdup.o strlen.o strnlen.o \ + strncat.o strlcpy.o strlcat.o \ + strstr.o strncmp.o strncpy.o strrchr.o \ + strxspn.o strspn.o strcspn.o strpbrk.o strsep.o strtok.o \ + strtok_r.o \ + fnmatch.o \ + gethostname.o getdomainname.o getcwd.o \ + seteuid.o setegid.o \ + getenv.o setenv.o putenv.o __put_env.o unsetenv.o \ + clearenv.o nullenv.o \ + getopt.o getopt_long.o readdir.o scandir.o alphasort.o remove.o \ + syslog.o closelog.o pty.o isatty.o reboot.o \ + gettimeofday.o settimeofday.o time.o \ + lseek.o nice.o getpriority.o \ + futimesat.o utime.o utimes.o \ + qsort.o bsearch.o \ + lrand48.o jrand48.o mrand48.o nrand48.o srand48.o seed48.o \ + inet/inet_ntoa.o inet/inet_aton.o inet/inet_addr.o \ + inet/inet_ntop.o inet/inet_pton.o inet/bindresvport.o \ + accept.o send.o recv.o \ + access.o chmod.o chown.o dup2.o mknod.o poll.o rename.o renameat.o \ + fstat.o fstatat.o lstat.o stat.o \ + lchown.o link.o rmdir.o unlink.o mkdir.o \ + readlink.o realpath.o select.o symlink.o pipe.o \ + ctype/isalnum.o ctype/isalpha.o ctype/isascii.o \ + ctype/isblank.o ctype/iscntrl.o ctype/isdigit.o \ + ctype/isgraph.o ctype/islower.o ctype/isprint.o \ + ctype/ispunct.o ctype/isspace.o ctype/isupper.o \ + ctype/isxdigit.o ctype/tolower.o ctype/toupper.o \ + userdb/getgrgid.o userdb/getgrnam.o userdb/getpwnam.o \ + userdb/getpwuid.o userdb/root_group.o userdb/root_user.o \ + setmntent.o endmntent.o getmntent.o \ + stdio/fclose.o stdio/fopen.o stdio/fdopen.o \ + stdio/fread.o stdio/fwrite.o stdio/fflush.o \ + stdio/ungetc.o stdio/fgetc.o \ + stdio/fseek.o stdio/ftell.o stdio/rewind.o \ + stdio/fileno.o stdio/feof.o stdio/ferror.o stdio/clearerr.o \ + sysconf/sysconf.o + +klib-$(CONFIG_KLIBC_ERRLIST) += errlist.o + +ifeq ($(CONFIG_KLIBC_ERRLIST),y) +KLIBCCFLAGS_strerror.o += -DWITH_ERRLIST +endif + +# These pass a huge maximum length to the corresponding length-limiting +# functions +KLIBCCFLAGS_sprintf.o += $(call cc-option,-Wno-format-truncation, ) +KLIBCCFLAGS_vsprintf.o += $(call cc-option,-Wno-format-truncation, ) + +# Clang 14 optimises zalloc() to recursively call calloc(), without +# this option +KLIBCCFLAGS_zalloc.o += $(call cc-option,-fno-builtin-malloc, ) + +# sigsuspend.c includes <klibc/havesyscall.h> generated by syscalls/ +# build, so require that to build first +$(obj)/sigsuspend.o: $(obj)/syscalls/klib.list + +##### +# Shared definitions +LIBC := libc.a +SOLIB := libc.so +SOHASH := klibc.so +CRT0 := arch/$(KLIBCARCHDIR)/crt0.o +SHARED_STUB := shared-stub.o + +always := $(LIBC) +ifdef KLIBCSHAREDFLAGS +always += $(SOLIB) $(SOHASH) $(SHARED_STUB) +endif +LIBC := $(call objectify,$(LIBC)) +SOLIB := $(call objectify,$(SOLIB)) +SOHASH := $(call objectify,$(SOHASH)) +CRT0 := $(call objectify,$(CRT0)) +SHARED_STUB := $(call objectify,$(SHARED_STUB)) + +SOLIBHASH = $(shell cat $(SOLIB).hash) + +##### +# Readable errormessages extracted from src.. +targets += errlist.c +quiet_cmd_errlist = GEN $@ + cmd_errlist = $(PERL) $< $(call flags,KLIBCCPPFLAGS) -errlist > $@ \ + || rm -f $@ + +$(obj)/errlist.c: $(srctree)/$(src)/makeerrlist.pl + $(call cmd,errlist) + + +# all .o files for all dirs +klib-o-files = $(shell cat $(obj)/klib.list \ + $(addsuffix /klib.list, $(klib-dirs))) +###### +# Build static library: libc.a +targets += libc.a __static_init.o +quiet_cmd_libc = KLIBCAR $@ + cmd_libc = rm -f $@; \ + $(KLIBCAR) Dcq $@ \ + $(call objectify,__static_init.o) $(klib-o-files); \ + $(KLIBCRANLIB) $@ + +$(LIBC): $(call objectify,__static_init.o) $(obj)/klib.list FORCE + $(call if_changed,libc) + +###### +# Build shared library +targets += libc.so __shared_init.o + +quiet_cmd_libcso = KLIBCLD $@ + cmd_libcso = $(KLIBCLD) $(KLIBCLDFLAGS) $(KLIBCSHAREDFLAGS) -o $@ \ + --start-group \ + $(CRT0) \ + $(call objectify,__shared_init.o) \ + $(klib-o-files) \ + $(KLIBCLIBGCC) \ + --end-group + +$(SOLIB): $(call objectify,__shared_init.o) $(obj)/klib.list FORCE + $(call if_changed,libcso) + + +##### +# Build sha1 hash values +targets += klibc.so libc.so.hash +hostprogs-y := sha1hash +clean-files += klibc-???????????????????????????.so + +quiet_cmd_solibhash = HASH $@ + cmd_solibhash = $(KLIBCNM) $< | egrep '^[0-9a-fA-F]+ [ADRTW] ' | \ + sort | $(obj)/sha1hash > $@ +$(SOLIB).hash: $(SOLIB) $(obj)/sha1hash FORCE + $(call if_changed,solibhash) + +quiet_cmd_sohash = GEN $@ + cmd_sohash = cat $< > $@; \ + $(KLIBCSTRIP) $(KLIBCSTRIPFLAGS) $@; \ + chmod a+x $@; \ + rm -f $(obj)/klibc-???????????????????????????.so; \ + ln -f $@ $(obj)/klibc-$(SOLIBHASH).so +$(SOHASH): $(SOLIB) $(SOLIB).hash + $(call cmd,sohash) + + +##### +# build shared-stub.o +targets += shared-stub.o + +quiet_cmd_interp = BUILD $@ + cmd_interp = $(KLIBCCC) $(klibccflags) -D__ASSEMBLY__ \ + -DLIBDIR=\"$(SHLIBDIR)\" \ + -DSOHASH=\"$(SOLIBHASH)\" \ + -c -o $@ $< + +$(SHARED_STUB): $(obj)/shared-stub.S $(SOLIB).hash + $(call if_changed,interp) + +##### +# Install klibc +install-rule: + @echo " INSTALL klibc to $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)lib" + $(Q)$(foreach f, $(LIBC) $(CRT0), \ + $(shell $(install-data) $(f) \ + $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)lib)) +ifdef KLIBCSHAREDFLAGS + $(Q)$(foreach f, $(SOLIB) $(SHARED_STUB), \ + $(shell $(install-data) $(f) \ + $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)lib)) + $(Q)$(install-lib) $(obj)/klibc-$(SOLIBHASH).so \ + $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)lib + $(Q)$(install-lib) $(obj)/klibc-$(SOLIBHASH).so \ + $(INSTALLROOT)$(SHLIBDIR) +endif + +# Directories to visit during clean and install +subdir- := arch/$(KLIBCARCHDIR)/ diff --git a/usr/klibc/LICENSE b/usr/klibc/LICENSE new file mode 100644 index 0000000..aa6d7a7 --- /dev/null +++ b/usr/klibc/LICENSE @@ -0,0 +1,73 @@ +This license applies to all files in directory and its subdirectories, +unless otherwise noted in individual files. + + +Some files are derived from files derived from the include/ directory +of the Linux kernel, and are licensed under the terms of the GNU +General Public License, version 2, as released by the Free Software +Foundation, Inc.; incorporated herein by reference. + + ----- + +Some files are derived from files copyrighted by the Regents of The +University of California, and are available under the following +license: + +Note: The advertising clause in the license appearing on BSD Unix +files was officially rescinded by the Director of the Office of +Technology Licensing of the University of California on July 22 +1999. He states that clause 3 is "hereby deleted in its entirety." + + * Copyright (c) + * The Regents of the University of California. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + + ----- + +For all remaining files, the following license applies: + + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * Any copyright notice(s) and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/usr/klibc/README.klibc b/usr/klibc/README.klibc new file mode 100644 index 0000000..1a9e09f --- /dev/null +++ b/usr/klibc/README.klibc @@ -0,0 +1,147 @@ +Introduction +============ + +This is klibc, what is intended to be a minimalistic libc subset for +use with initramfs. It is deliberately written for small size, +minimal entanglement, and portability, not speed. It is definitely a +work in progress, and a lot of things are still missing. + +Building +======== + +The build procedure is not very polished yet, but for a native build +with the default configuration it should be simple: + +1. In a recent Linux kernel source directory, run: + make headers_install INSTALL_HDR_PATH=<klibc-source-dir>/linux +2. In the klibc source directory, run: + make + +Cross-compiling +--------------- + +If you're cross-compiling, you need to set KLIBCARCH to the +appropriate architecture, and set CROSS_COMPILE to your toolchain +prefix, on the "make" command line. You also need to set ARCH to the +appropriate kernel architecture name on the "make headers_install" +command line. + +IMPORTANT: if you're on a 64-bit machine with a 32-bit userland, and +you want to build the 32-bit version: you need to set KLIBCARCH to the +32-bit architecture. Building the 32-bit architecture usually (but +not always) produces smaller binaries. + +If you are on ARM, and want to build a thumb version of the library +(this is supported), change OPTFLAGS in arch/arm/MCONFIG to build +thumb code. + +Out-of-tree builds +------------------ + +It is possible to use a separate build directory so that build +products are not created in source directories. To do this, +create the build directory and in that directory run: + make -f <source-dir>/Makefile KBUILD_SRC=<source-dir> + +Build configuration +------------------- + +The build configuration is defined in a ".config" file in the +build directory. If this file does not already exist, it is +created as a copy of the "defconfig" source file. + +The configuration variables are: + +* CONFIG_KLIBC_ERRLIST (bool): Include standard error strings for + strerror(). If disabled, strerror() returns the error number as a + string. +* CONFIG_KLIBC_ZLIB (bool): Include zlib decompression functions. If + disabled, kinit can only load uncompressed ramdisk images. +* CONFIG_KLIBC_ZIP (bool): Include compression support in the gzip + command. +* CONFIG_DEBUG_INFO (bool): Install all executables and the shared + library with debug information and build IDs included. If + disabled, the executables and shared library are stripped when + installed. + +For arm only: + +* CONFIG_KLIBC_THUMB (bool): Compile all code to Thumb instructions, + which should reduce code size slightly. If disabled, regular ARM + instructions are used. +* CONFIG_AEABI (bool): Compile ARM code to use the ARM EABI and the + "new" system call ABI. + +If both CONFIG_KLIBC_THUMB and CONFIG_AEABI are disabled, the GNU APCS +and the old system call ABI are used. + +For i386 only: + +* CONFIG_REGPARM (bool): Optimise function calls by passing the first + 3 function parameters in registers. + +For mips64 only: + +* CONFIG_KLIBC_MIPS_USE_CB (bool): Use compact branch instructions. + This should be enabled when targetting MIPS R6 and must be disabled + for older MIPS ISAs. + +Building without kernel source +------------------------------ + +If you already have a copy of the current kernel UAPI headers, you +don't need the kernel source as well. You can either: + +1. Copy or link to the UAPI headers so that they appear under the + "linux/include" subdirectory. +2. Set the KLIBCKERNELSRC variable on the "make" command line to + point to the *parent* of the UAPI headers directory. + +Architecture support +==================== + +The following is the last known status of various architectures: + + alpha: Working + arm-thumb: Working + arm: Working + arm64: Working + h8300: Not yet ported + i386: Working + ia64: Working + m68k: Working + mips: Working + mips64: Working + parisc: Working + parisc64: Not yet ported + ppc: Working + ppc64: Working + riscv64: Working + s390: Working + s390x: Working + sh: Working + sparc: Working + sparc64: Working + x86-64: Working + xtensa: Not yet ported + +Shared library support requires recent binutils on many architectures. + +Note that even the "working" ones likely have bugs. Please report +them if you run into them. + +Testing +======= + +Try the test programs in the tests/ directory. They should run... + +Contact +======= + +Contact the klibc mailing list: + + https://www.zytor.com/mailman/listinfo/klibc + +... for more info. + + -hpa diff --git a/usr/klibc/SOCKETCALLS.def b/usr/klibc/SOCKETCALLS.def new file mode 100644 index 0000000..97413de --- /dev/null +++ b/usr/klibc/SOCKETCALLS.def @@ -0,0 +1,22 @@ +/* -*- c -*- + * + * These are calls that are invoked via the socketcall mechanism + * Note that on most architectures this is simply #included into + * SYSCALLS.def. + */ +<?> int socket(int, int, int); +<?> int bind(int, const struct sockaddr *, int); +<?> int connect(int, const struct sockaddr *, socklen_t); +<?> int listen(int, int); +<?> int accept(int, struct sockaddr *, socklen_t *); +<?> int accept4(int, struct sockaddr *, socklen_t *, int); +<?> int getsockname(int, struct sockaddr *, socklen_t *); +<?> int getpeername(int, struct sockaddr *, socklen_t *); +<?> int socketpair(int, int, int, int *); +<?> int sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); +<?> int recvfrom(int, void *, size_t, unsigned int, struct sockaddr *, socklen_t *); +<?> int shutdown(int, int); +<?> int setsockopt(int, int, int, const void *, socklen_t); +<?> int getsockopt(int, int, int, void *, socklen_t *); +<?> int sendmsg(int, const struct msghdr *, unsigned int); +<?> int recvmsg(int, struct msghdr *, unsigned int); diff --git a/usr/klibc/SYSCALLS.def b/usr/klibc/SYSCALLS.def new file mode 100644 index 0000000..9b6b112 --- /dev/null +++ b/usr/klibc/SYSCALLS.def @@ -0,0 +1,264 @@ +/* -*- c -*- + * + * This is a list of system calls we invoke "directly". These + * are generated into syscall stubs in their own files, so the + * linker can do its job properly. + * + * The full description of a line is: + * [<[?][!]arch,...>] type [sysname,...][@systype][::funcname](args); + * + * ? means only instantiate this system call if present in asm/unistd.h + */ + +#include <asm/unistd.h> +#include <klibc/sysconfig.h> +#include <bitsize.h> + +/* + * Process-related syscalls + */ +void _exit,exit::_exit(int); +<?!ia64> pid_t clone::__clone(unsigned long, void *); +<?ia64> pid_t clone::__clone2(unsigned long, void *, void *); +# if ! _KLIBC_NO_MMU +<!sparc,sparc64,ia64,arm64,riscv64,loongarch64> pid_t fork(); +<sparc,sparc64> pid_t fork@forkish(); +#endif +#if _KLIBC_REAL_VFORK +/* + * A lot of architectures need architecture-specific vfork + * stubs, due to the no-stack requirement. These are the + * architectures which do not. + */ +<alpha,ppc,ppc64,sh,s390,s390x> pid_t vfork(); +<sparc,sparc64> pid_t vfork@forkish(); +#endif +<!alpha> pid_t getpid(); +<alpha> pid_t getxpid@dual0::getpid(); +int setpgid(pid_t, pid_t); +pid_t getpgid(pid_t); +<!alpha> pid_t getppid(); +<alpha> pid_t getxpid@dual1::getppid(); +pid_t setsid(); +pid_t getsid(pid_t); +pid_t wait4(pid_t, int *, int, struct rusage *); +int execve(const char *, char * const *, char * const *); +<?> int nice(int); +<alpha,ia64> int getpriority(int, int); +<!alpha,ia64> int getpriority::__getpriority(int, int); +int setpriority(int, int, int); +int getrusage(int, struct rusage *); +int sched_setscheduler(pid_t, int, const struct sched_param *); +<?> int sched_setaffinity(pid_t, unsigned int, unsigned long *); +<?> int sched_getaffinity(pid_t, unsigned int, unsigned long *); +int sched_yield(); +<i386> int prctl@varadic(int, unsigned long, unsigned long, unsigned long, unsigned long); +<!i386> int prctl(int, unsigned long, unsigned long, unsigned long, unsigned long); + +/* + * User and group IDs + */ +int setuid32,setuid::setuid(uid_t); +int setgid32,setgid::setgid(gid_t); +<!alpha> uid_t getuid32,getuid::getuid(); +<alpha> uid_t getxuid@dual0::getuid(); +<!alpha> gid_t getgid32,getgid::getgid(); +<alpha> gid_t getxgid@dual0::getgid(); +<!alpha> uid_t geteuid32,geteuid::geteuid(); +<alpha> uid_t getxuid@dual1::geteuid(); +<!alpha> gid_t getegid32,getegid::getegid(); +<alpha> gid_t getxgid@dual1::getegid(); +int getgroups32,getgroups::getgroups(int, gid_t *); +int setgroups32,setgroups::setgroups(size_t, const gid_t *); +int setreuid32,setreuid::setreuid(uid_t, uid_t); +int setregid32,setregid::setregid(gid_t, gid_t); +int setfsuid32,setfsuid::setfsuid(uid_t); +int setfsgid32,setfsgid::setfsgid(gid_t); +int setresuid32,setresuid::setresuid(int, uid_t, uid_t, uid_t); + +/* + * POSIX Capabilities + */ +int capget(cap_user_header_t, cap_user_data_t); +int capset(cap_user_header_t, cap_user_data_t); + +/* + * Filesystem-related system calls + */ +int mount(const char *, const char *, const char *, unsigned long, const void *); +<!alpha,ia64> int umount2(const char *, int); +<alpha,ia64> int umount::umount2(const char *, int); +<?> int pivot_root(const char *, const char *); +int sync(); +#ifdef __NR_statfs64 +int statfs64::__statfs64(const char *, size_t, struct statfs *); +#else +int statfs(const char *, struct statfs *); +#endif +#ifdef __NR_fstatfs64 +int fstatfs64::__fstatfs64(int, size_t, struct statfs *); +#else +int fstatfs(int, struct statfs *); +#endif +int swapon(const char *, int); +int swapoff(const char *); + +/* + * Inode-related system calls + */ +<?> int access(const char *, int); +int faccessat(int, const char *, int, int); +<?> int link(const char *, const char *); +<?> int linkat(int, const char *, int, const char *, int); +<?> int unlink(const char *); +<?> int unlinkat(int, const char *, int); +int chdir(const char *); +int fchdir(int); +<?> int rename(const char *, const char *); +<?> int renameat(int, const char *, int, const char *); +<?> int renameat2(int, const char *, int, const char *, unsigned int); +<?> int mknod(const char *, mode_t, dev_t); +<?> int mknodat(int, const char *, mode_t, dev_t); +<?> int chmod(const char *, mode_t); +int fchmod(int, mode_t); +<?> int fchmodat(int, const char *, mode_t, int); +<?> int mkdir(const char *, mode_t); +<?> int mkdirat(int, const char *, mode_t); +<?> int rmdir(const char *); +<?!alpha,ia64,mips,mips64,sh,sparc,sparc64> int pipe(int *); +int pipe2(int *, int); +mode_t umask(mode_t); +int chroot(const char *); +<?> int symlink(const char *, const char *); +<?> int symlinkat(const char *, int, const char *); +<?> int readlink(const char *, char *, size_t); +<?> int readlinkat(int, const char *, char *, int); +int statx(int, const char *, int, unsigned int, struct statx *); +int getdents64,getdents::getdents(unsigned int, struct dirent *, unsigned int); +<?> int chown32,chown::chown(const char *, uid_t, gid_t); +int fchown32,fchown::fchown(int, uid_t, gid_t); +<?> int fchownat(int, const char *, uid_t, gid_t, int); +<?> int lchown32,lchown::lchown(const char *, uid_t, gid_t); +int getcwd::__getcwd(char *, size_t); +<32> int utimensat_time64::utimensat(int, const char *, const struct timespec *, int); +<64> int utimensat(int, const char *, const struct timespec *, int); +<?> int inotify_init(); +<?> int inotify_add_watch(int, const char *, __u32); +<?> int inotify_rm_watch(int, __u32); + +/* + * I/O operations + */ +<!i386,m68k,64> int open::__open(const char *, int, mode_t); +<?!i386,m68k,64> int openat::__openat(int, const char *, int, mode_t); +<?64> int open(const char *, int, mode_t); +<64> int openat(int, const char *, int, mode_t); +ssize_t read(int, void *, size_t); +ssize_t write(int, const void *, size_t); +int close(int); +<64> off_t lseek(int, off_t, int); +<32> int _llseek::__llseek(int, unsigned long, unsigned long, off_t *, int); +int dup(int); +<?> int dup2(int, int); +int dup3(int, int, int); +<i386> int fcntl64@varadic::fcntl(int, int, unsigned long); +<ppc64> int fcntl(int, int, unsigned long); +<!i386,ppc64> int fcntl64,fcntl::fcntl(int, int, unsigned long); +int ioctl(int, int, void *); +int flock(int, int); +<32> int pselect6_time64::__pselect6(int, fd_set *, fd_set *, fd_set *, struct timespec *, const struct __pselect6 *); +<64> int pselect6::__pselect6(int, fd_set *, fd_set *, fd_set *, struct timespec *, const struct __pselect6 *); +<?> int poll(struct pollfd *, nfds_t, long); +<32> int ppoll_time64::__ppoll(struct pollfd *, nfds_t, struct timespec *, const sigset_t *, size_t); +<64> int ppoll::__ppoll(struct pollfd *, nfds_t, struct timespec *, const sigset_t *, size_t); +int fsync(int); +int fdatasync,fsync::fdatasync(int); +int readv(int, const struct iovec *, int); +int writev(int, const struct iovec *, int); +int ftruncate64,ftruncate::ftruncate(int, off_t); +<parisc> ssize_t pread64,pread::__pread(int, void *, size_t, off_t); +<parisc> ssize_t pwrite64,pwrite::__pwrite(int, void *, size_t, off_t); +<!parisc> ssize_t pread64,pread::pread(int, void *, size_t, off_t); +<!parisc> ssize_t pwrite64,pwrite::pwrite(int, void *, size_t, off_t); +int sync_file_range,fdatasync,fsync::sync_file_range(int, off_t, off_t, unsigned int); +<?> int splice(int, off_t *, int, off_t *, size_t, unsigned int); +<?> int tee(int, int, size_t, unsigned int); +ssize_t sendfile64,sendfile::sendfile(int, int, off_t *, size_t, off_t); + +/* + * Signal operations + */ +<!sparc,sparc64,alpha,ia64> int rt_sigaction::__rt_sigaction(int, const struct sigaction *, struct sigaction *, size_t); +<sparc,sparc64> int rt_sigaction::____rt_sigaction(int, const struct sigaction *, struct sigaction *, void *, size_t); +<alpha> int rt_sigaction::____rt_sigaction(int, const struct sigaction *, struct sigaction *, size_t, void *); +<ia64> int rt_sigaction::____rt_sigaction(int, const struct sigaction *, struct sigaction *, size_t); +int rt_sigsuspend::__rt_sigsuspend(const sigset_t *, size_t); +int rt_sigpending::__rt_sigpending(sigset_t *, size_t); +int rt_sigprocmask::__rt_sigprocmask(int, const sigset_t *, sigset_t *, size_t); +<s390,s390x,sparc,sparc64> void rt_sigreturn::__sigreturn(); +int kill(pid_t, int); +<?> unsigned int alarm(unsigned int); +int getitimer(int, struct itimerval *); +int setitimer(int, const struct itimerval *, struct itimerval *); + +/* + * Time-related system calls + */ +clock_t times(struct tms *); +int gettimeofday::__gettimeofday(void *, struct timezone *); +int settimeofday::__settimeofday(const void *, const struct timezone *); +<32> int clock_gettime64::clock_gettime(clockid_t, struct timespec *); +<64> int clock_gettime(clockid_t, struct timespec *); +<32> int clock_settime64::clock_settime(clockid_t, const struct timespec *); +<64> int clock_settime(clockid_t, const struct timespec *); +<32> int clock_nanosleep_time64::__clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *); +<64> int clock_nanosleep::__clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *); +<?> int pause(); + +/* + * Memory + */ +<?> void * brk::__brk(void *); +int munmap(void *, size_t); +void * mremap(void *, size_t, size_t, unsigned long); +int msync(const void *, size_t, int); +int mprotect(const void *, size_t, int); +# if _KLIBC_USE_MMAP2 +<!s390> void * mmap2::__mmap2(void *, size_t, int, int, int, long); +# else +<!s390x> void * mmap(void *, size_t, int, int, int, long); +int mlockall(int); +int munlockall(); +int mlock(const void *, size_t); +int munlock(const void *, size_t); +#endif + +/* + * System stuff + */ +int uname(struct utsname *); +int setdomainname(const char *, size_t); +int sethostname(const char *, size_t); +long init_module(void *, unsigned long, const char *); +long finit_module(int, const char *, int); +long delete_module(const char *, unsigned int); +int reboot::__reboot(int, int, int, void *); +int syslog::klogctl(int, char *, int); +int sysinfo(struct sysinfo *); +long kexec_load(void *, unsigned long, struct kexec_segment *, unsigned long); + +/* + * Low-level I/O (generally architecture-specific); + */ +<i386,x86_64> int iopl(int); +<i386,x86_64> int ioperm(unsigned long, unsigned long, int); +<i386> int vm86(struct vm86_struct *); + +/* + * Most architectures have the socket interfaces using regular + * system calls. + */ +<?!i386> long socketcall::__socketcall(int, const unsigned long *); +#if ! _KLIBC_SYS_SOCKETCALL +#include "SOCKETCALLS.def" +#endif diff --git a/usr/klibc/__put_env.c b/usr/klibc/__put_env.c new file mode 100644 index 0000000..30d415c --- /dev/null +++ b/usr/klibc/__put_env.c @@ -0,0 +1,74 @@ +/* + * __put_env.c - common code for putenv() and setenv() + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "env.h" + +static size_t __environ_size; +static char **__environ_alloc; + +/* str should be a duplicated version of the input string; + len is the length of the key including the = sign */ + +int __put_env(char *str, size_t len, int overwrite) +{ + static char *const null_environ = { NULL }; + char **p, *q; + char **newenv; + size_t n; + + if (!environ) + environ = (char **)null_environ; + + n = 1; /* Include space for final NULL */ + for (p = environ; (q = *p); p++) { + n++; + if (!strncmp(q, str, len)) { + if (!overwrite) + free(str); + else + *p = str; /* Possible memory leak... */ + return 0; + } + } + + if (__environ_alloc && environ != __environ_alloc) { + free(__environ_alloc); + __environ_alloc = NULL; + } + + /* Need to extend the environment */ + if (n < __environ_size) { + p[1] = NULL; + *p = str; + return 0; + } else { + if (__environ_alloc) { + newenv = + realloc(__environ_alloc, + (__environ_size << 1) * sizeof(char *)); + if (!newenv) + return -1; + + __environ_size <<= 1; + } else { + /* Make a reasonable guess how much more space + we need */ + size_t newsize = n + 32; + newenv = malloc(newsize * sizeof(char *)); + if (!newenv) + return -1; + + memcpy(newenv, environ, n * sizeof(char *)); + __environ_size = newsize; + } + newenv[n-1] = str; /* Old NULL position */ + newenv[n] = NULL; + environ = newenv; + } + return 0; +} diff --git a/usr/klibc/__shared_init.c b/usr/klibc/__shared_init.c new file mode 100644 index 0000000..592a3db --- /dev/null +++ b/usr/klibc/__shared_init.c @@ -0,0 +1,2 @@ +#define SHARED 1 +#include "libc_init.c" diff --git a/usr/klibc/__signal.c b/usr/klibc/__signal.c new file mode 100644 index 0000000..d174b8e --- /dev/null +++ b/usr/klibc/__signal.c @@ -0,0 +1,20 @@ +/* + * __signal.c + */ + +#include <signal.h> + +__sighandler_t __signal(int signum, __sighandler_t handler, int flags) +{ + struct sigaction sa; + + sa.sa_handler = handler; + sa.sa_flags = flags; + sigemptyset(&sa.sa_mask); + + if (sigaction(signum, &sa, &sa)) { + return (__sighandler_t) SIG_ERR; + } else { + return (__sighandler_t) sa.sa_handler; + } +} diff --git a/usr/klibc/__static_init.c b/usr/klibc/__static_init.c new file mode 100644 index 0000000..0b59eed --- /dev/null +++ b/usr/klibc/__static_init.c @@ -0,0 +1,2 @@ +#define SHARED 0 +#include "libc_init.c" diff --git a/usr/klibc/abort.c b/usr/klibc/abort.c new file mode 100644 index 0000000..4629a17 --- /dev/null +++ b/usr/klibc/abort.c @@ -0,0 +1,18 @@ +/* + * abort.c + */ + +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> + +void abort(void) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGABRT); + sigprocmask(SIG_UNBLOCK, &set, NULL); + raise(SIGABRT); + _exit(255); /* raise() should have killed us */ +} diff --git a/usr/klibc/accept.c b/usr/klibc/accept.c new file mode 100644 index 0000000..6057544 --- /dev/null +++ b/usr/klibc/accept.c @@ -0,0 +1,16 @@ +/* + * accept.c + * + * Some architectures need to wrap the system call + */ + +#include <sys/socket.h> + +#if !_KLIBC_SYS_SOCKETCALL && defined(__NR_accept4) && !defined(__NR_accept) + +int accept(int socket, struct sockaddr *address, socklen_t *addr_len) +{ + return accept4(socket, address, addr_len, 0); +} + +#endif diff --git a/usr/klibc/access.c b/usr/klibc/access.c new file mode 100644 index 0000000..0f24856 --- /dev/null +++ b/usr/klibc/access.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_access + +int access(const char *pathname, int mode) +{ + return faccessat(AT_FDCWD, pathname, mode, 0); +} + +#endif /* __NR_access */ diff --git a/usr/klibc/alarm.c b/usr/klibc/alarm.c new file mode 100644 index 0000000..8bd74c5 --- /dev/null +++ b/usr/klibc/alarm.c @@ -0,0 +1,25 @@ +/* + * alarm.c + */ + +#include <sys/time.h> +#include <sys/syscall.h> + +#ifndef __NR_alarm + +/* Emulate alarm() via setitimer() */ + +unsigned int alarm(unsigned int seconds) +{ + struct itimerval iv; + + iv.it_interval.tv_sec = iv.it_interval.tv_usec = 0; + iv.it_value.tv_sec = seconds; + iv.it_value.tv_usec = 0; + + setitimer(ITIMER_REAL, &iv, &iv); + + return iv.it_value.tv_sec + (iv.it_value.tv_usec ? 1 : 0); +} + +#endif diff --git a/usr/klibc/alphasort.c b/usr/klibc/alphasort.c new file mode 100644 index 0000000..d420495 --- /dev/null +++ b/usr/klibc/alphasort.c @@ -0,0 +1,12 @@ +/* + * alphasort.c: alphasort + */ + +#include <string.h> + +#include <dirent.h> + +int alphasort(const struct dirent **a, const struct dirent **b) +{ + return strcmp((*a)->d_name, (*b)->d_name); +} diff --git a/usr/klibc/arch/README.klibc.arch b/usr/klibc/arch/README.klibc.arch new file mode 100644 index 0000000..662b9d3 --- /dev/null +++ b/usr/klibc/arch/README.klibc.arch @@ -0,0 +1,81 @@ +To port klibc to a new architecture, you need: + +a) A directory structure + +Each archtecture has a klibc/arch/ directory, which should include an +MCONFIG and a Makefile.inc file, and an include/arch/ directory, which +includes some architecture-specific header files, including +klibc/archconfig.h. + + +b) Architecture-specific configuration + (include/arch/*/klibc/sysconfig.h) + +This file can set configuration variables from +include/klibc/sysconfig.h. + + +c) Startup code + (klibc/arch/*/crt0.S) + +The crt0.S assembly routine typically corresponds to the following +pseudo-C code. In addition, each architecture needs any support +routines that gcc-generated code expects to find in the system library +-- Alpha, for example, needs divide subroutines. + +The "getenvtest" test program is a very good test for proper crt0.S +functionality. + + +extern __noreturn __libc_init(void *, void *); + +__noreturn _start(void) +{ + void *elf_data = get_elf_data_address(); /* Usually the stack address */ + void *atexit_ptr = get_atexit_ptr(); /* Usually in a register */ + + /* Some architectures need this for debugging to work */ + setup_null_stack_frame_if_necessary(); + + __libc_init(elf_data, atexit_ptr); +} + + +d) A setenv implementation + (klibc/arch/*/setjmp.S, include/arch/*klibc/archsetjmp.h) + +On most (but not all!) architectures, this entails creating a setjmp +buffer big enough to hold all callee-saved registers, plus the stack +pointer and the return address. In setjmp.S you have: + +* A "setjmp" function that writes out the callee-saved registers, the + stack pointer and the return address to the buffer pointed to by the + first argument, and then returns zero normally. + + On some architectures you need to take some kind of action to make + sure the contents of the stack is actually manifest in memory and + not cached in the CPU. In some cases (e.g. on SPARC) this will + automatically spill the registers onto the stack; then they don't + need to be spilled into the jmp_buf. + +* A "longjmp" function that read back these same registers from the + jmp_buf pointed to by the first argument, and returns the second + argument *to the address specified in the jmp_buf*. + + On some architectures you need to take some kind of action to flush + any cached stack data or return stack. + + +e) Any support functions needed by gcc, *unless* they are in libgcc + *and* libgcc is usable for klibc on your particular platform. If + libgcc isn't usable for klibc (on MIPS, for example, libgcc is + compiled in a way that is not compatible with klibc) there are + reasonably good clones of most of the libgcc functions in the libgcc + directory. To use them, add them to ARCHOBJS in + klibc/arch/*/Makefile.inc. + + +f) A link location for the shared klibc. This should be specified in + SHAREDFLAGS in klibc/arch/*/MCONFIG. + + This is not applicable to no-MMU architectures. diff --git a/usr/klibc/arch/alpha/Kbuild b/usr/klibc/arch/alpha/Kbuild new file mode 100644 index 0000000..715a430 --- /dev/null +++ b/usr/klibc/arch/alpha/Kbuild @@ -0,0 +1,53 @@ +# -*- makefile -*- +# +# arch/alpha/Makefile.inc +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +always := crt0.o +targets := crt0.o +klib-y := pipe.o sigaction.o setjmp.o sigreturn.o syscall.o sysdual.o + +# Special CFLAGS for the divide code +DIVCFLAGS = $(KLIBCREQFLAGS) $(KLIBCARCHREQFLAGS) \ + -O3 -fomit-frame-pointer -fcall-saved-1 -fcall-saved-2 \ + -fcall-saved-3 -fcall-saved-4 -fcall-saved-5 -fcall-saved-6 \ + -fcall-saved-7 -fcall-saved-8 -ffixed-15 -fcall-saved-16 \ + -fcall-saved-17 -fcall-saved-18 -fcall-saved-19 -fcall-saved-20 \ + -fcall-saved-21 -fcall-saved-22 -ffixed-23 -fcall-saved-24 \ + -ffixed-25 -ffixed-27 + +div-objs := __divqu.o __remqu.o __divq.o __remq.o +div-objs += __divlu.o __remlu.o __divl.o __reml.o +klib-y += $(div-objs) + +quiet_cmd_regswap = REGSWAP $@ + cmd_regswap = sed -e 's/\$$0\b/$$27/g' -e 's/\$$24\b/$$99/g' \ + -e 's/\$$16\b/$$24/g' -e 's/\$$17\b/$$25/g' \ + -e 's/\$$26\b/$$23/g' -e 's/\$$99\b/$$16/g' < $< > $@ + +# Use static pattern rule to avoid using a temporary file +$(addprefix $(obj)/,$(div-objs:.o=.S)): $(obj)/%.S: $(obj)/%.ss + $(call if_changed,regswap) + +quiet_cmd_genss = DIV-CC $@ + cmd_genss = $(CC) $(DIVCFLAGS) $(FILE_CFLAGS) \ + $(call flags,KLIBCCPPFLAGS) \ + -DNAME=$(basename $(notdir $@)) -S -o $@ $< + +$(obj)/%.ss: $(obj)/divide.c + $(call if_changed,genss) + +$(obj)/__divqu.ss: FILE_CFLAGS := -DSIGNED=0 -DREM=0 -DBITS=64 +$(obj)/__remqu.ss: FILE_CFLAGS := -DSIGNED=0 -DREM=1 -DBITS=64 +$(obj)/__divq.ss: FILE_CFLAGS := -DSIGNED=1 -DREM=0 -DBITS=64 +$(obj)/__remq.ss: FILE_CFLAGS := -DSIGNED=1 -DREM=1 -DBITS=64 +$(obj)/__divlu.ss: FILE_CFLAGS := -DSIGNED=0 -DREM=0 -DBITS=32 +$(obj)/__remlu.ss: FILE_CFLAGS := -DSIGNED=0 -DREM=1 -DBITS=32 +$(obj)/__divl.ss: FILE_CFLAGS := -DSIGNED=1 -DREM=0 -DBITS=32 +$(obj)/__reml.ss: FILE_CFLAGS := -DSIGNED=1 -DREM=1 -DBITS=32 + +targets += $(div-objs:.o=.S) $(div-objs:.o=.ss) diff --git a/usr/klibc/arch/alpha/MCONFIG b/usr/klibc/arch/alpha/MCONFIG new file mode 100644 index 0000000..64847b7 --- /dev/null +++ b/usr/klibc/arch/alpha/MCONFIG @@ -0,0 +1,19 @@ +# -*- makefile -*- +# +# arch/alpha/MCONFIG +# +# Build configuration for this architecture +# + +KLIBCOPTFLAGS += -Os +KLIBCBITSIZE = 64 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 7 GB - normal binaries start at 4.5 GB, and the stack is below +# the binary. +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x1c0000000 + +# Kernel uses our sa_restorer for signal return +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/alpha/README-gcc b/usr/klibc/arch/alpha/README-gcc new file mode 100644 index 0000000..1d0052f --- /dev/null +++ b/usr/klibc/arch/alpha/README-gcc @@ -0,0 +1,22 @@ + The current Alpha chips don't provide hardware for integer + division. The C compiler expects the functions + + __divqu: 64-bit unsigned long divide + __remqu: 64-bit unsigned long remainder + __divq/__remq: signed 64-bit + __divlu/__remlu: unsigned 32-bit + __divl/__reml: signed 32-bit + + These are not normal C functions: instead of the normal calling + sequence, these expect their arguments in registers t10 and t11, and + return the result in t12 (aka pv). Register AT may be clobbered + (assembly temporary), anything else must be saved. + + Furthermore, the return address is in t9 instead of ra. + + Normal function Divide functions + --------------- ---------------- + v0 ($0) t12/pv ($27) + a0 ($16) t10 ($24) + a1 ($17) t11 ($25) + ra ($26) t9 ($23) diff --git a/usr/klibc/arch/alpha/crt0.S b/usr/klibc/arch/alpha/crt0.S new file mode 100644 index 0000000..5e2babb --- /dev/null +++ b/usr/klibc/arch/alpha/crt0.S @@ -0,0 +1,22 @@ +# +# arch/alpha/crt0.S +# + + .text + .type _start,@function + .ent _start, 0 + .globl _start +_start: + .frame $30, 0, $26, 0 + mov $31, $15 + br $29, 1f +1: ldgp $29, 0($29) + .prologue 0 + + lda $16, 0($30) # ELF data structure + lda $17, 0($0) # atexit pointer + + jsr $26, __libc_init + + .size _start,.-_start + .end _start diff --git a/usr/klibc/arch/alpha/divide.c b/usr/klibc/arch/alpha/divide.c new file mode 100644 index 0000000..c44254f --- /dev/null +++ b/usr/klibc/arch/alpha/divide.c @@ -0,0 +1,59 @@ +#include <stdint.h> +#include <asm/gentrap.h> +#include <asm/pal.h> + +#if BITS == 64 +typedef uint64_t uint; +typedef int64_t sint; +#else +typedef uint32_t uint; +typedef int32_t sint; +#endif + +#ifdef SIGNED +typedef sint xint; +#else +typedef uint xint; +#endif + +xint NAME(uint num, uint den) +{ + uint quot = 0, qbit = 1; + int minus = 0; + xint v; + + if (den == 0) { + /* This is really $16, but $16 and $24 are exchanged by a script */ + register unsigned long cause asm("$24") = GEN_INTDIV; + asm volatile ("call_pal %0"::"i" (PAL_gentrap), "r"(cause)); + return 0; /* If trap returns... */ + } +#if SIGNED + if ((sint) (num ^ den) < 0) + minus = 1; + if ((sint) num < 0) + num = -num; + if ((sint) den < 0) + den = -den; +#endif + + /* Left-justify denominator and count shift */ + while ((sint) den >= 0) { + den <<= 1; + qbit <<= 1; + } + + while (qbit) { + if (den <= num) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + v = (xint) (REM ? num : quot); + if (minus) + v = -v; + return v; +} diff --git a/usr/klibc/arch/alpha/pipe.S b/usr/klibc/arch/alpha/pipe.S new file mode 100644 index 0000000..ee72413 --- /dev/null +++ b/usr/klibc/arch/alpha/pipe.S @@ -0,0 +1,38 @@ +# +# arch/alpha/pipe.S +# + +# +# pipe() on alpha returns both file descriptors in registers -- +# $0 (v0) and $20 (a4) respectively. This is unlike any other system call, +# as far as I can tell. +# + +#include <asm/unistd.h> +#include <machine/asm.h> + + .text + .align 3 + .type pipe, @function + .ent pipe, 0 + .globl pipe +pipe: + .frame sp,0,ra,0 + lda v0, __NR_pipe + callsys + beq a3, 1f + br pv, 2f # pv <- pc +2: + ldgp gp, 0(pv) + lda a1, errno + lda v0, -1(zero) + stl a3, 0(a1) + ret zero,(ra),1 +1: + stl v0, 0(a0) + lda v0, 0 + stl a4, 4(a0) + ret zero,(ra),1 + + .size pipe,.-pipe + .end pipe diff --git a/usr/klibc/arch/alpha/setjmp.S b/usr/klibc/arch/alpha/setjmp.S new file mode 100644 index 0000000..ed604bd --- /dev/null +++ b/usr/klibc/arch/alpha/setjmp.S @@ -0,0 +1,75 @@ +# +# setjmp.S +# + +# +# The jmp_buf looks like: +# +# s0..5 +# fp +# ra +# gp +# sp +# + +#include <machine/asm.h> + + .text + .align 3 + .type setjmp,@function + .ent setjmp, 0 + .globl setjmp +setjmp: + lda v0, 0(zero) + stq s0, 0(a0) + stq s1, 8(a0) + stq s2, 16(a0) + stq s3, 24(a0) + stq s4, 32(a0) + stq s5, 40(a0) + stq fp, 48(a0) + stq ra, 56(a0) + stq gp, 64(a0) + stq sp, 72(a0) + stt $f2, 80(a0) + stt $f3, 88(a0) + stt $f4, 96(a0) + stt $f5, 104(a0) + stt $f6, 112(a0) + stt $f7, 120(a0) + stt $f8, 128(a0) + stt $f9, 136(a0) + ret zero,(ra),1 + + .size setjmp,.-setjmp + .end setjmp + + .type longjmp,@function + .ent longjmp, 0 + .globl longjmp +longjmp: + mov a1, v0 + ldq s0, 0(a0) + ldq s1, 8(a0) + ldq s2, 16(a0) + ldq s3, 24(a0) + ldq s4, 32(a0) + ldq s5, 40(a0) + ldq fp, 48(a0) + ldq ra, 56(a0) + ldq gp, 64(a0) + ldq sp, 72(a0) + ldt $f2, 80(a0) + ldt $f3, 88(a0) + ldt $f4, 96(a0) + ldt $f5, 104(a0) + ldt $f6, 112(a0) + ldt $f7, 120(a0) + ldt $f8, 128(a0) + ldt $f9, 136(a0) + /* We're bound to get a mispredict here, but at least give us + a chance to get the return stack back in sync... */ + ret zero,(ra),1 + + .size longjmp,.-longjmp + .end longjmp diff --git a/usr/klibc/arch/alpha/sigaction.c b/usr/klibc/arch/alpha/sigaction.c new file mode 100644 index 0000000..53e830b --- /dev/null +++ b/usr/klibc/arch/alpha/sigaction.c @@ -0,0 +1,16 @@ +/* + * sigaction.c + */ + +#include <signal.h> +#include <sys/syscall.h> + +__extern void __sigreturn(void); +__extern int ____rt_sigaction(int, const struct sigaction *, struct sigaction *, + size_t, void (*)(void)); + +int __rt_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact, size_t size) +{ + return ____rt_sigaction(sig, act, oact, size, &__sigreturn); +} diff --git a/usr/klibc/arch/alpha/sigreturn.S b/usr/klibc/arch/alpha/sigreturn.S new file mode 100644 index 0000000..a979b7a --- /dev/null +++ b/usr/klibc/arch/alpha/sigreturn.S @@ -0,0 +1,18 @@ +/* + * arch/alpha/sigreturn.S + */ + +#include <machine/asm.h> +#include <asm/unistd.h> + + .text + .align 3 + .type __sigreturn,@function + .ent __sigreturn,0 + .globl __sigreturn +__sigreturn: + mov sp,a0 # struct sigcontext on stack + lda v0,__NR_rt_sigreturn(zero) + callsys + .size __sigreturn,.-__sigreturn + .end __sigreturn diff --git a/usr/klibc/arch/alpha/syscall.S b/usr/klibc/arch/alpha/syscall.S new file mode 100644 index 0000000..ae69ef2 --- /dev/null +++ b/usr/klibc/arch/alpha/syscall.S @@ -0,0 +1,26 @@ +# +# arch/alpha/syscall.S +# + +#include <machine/asm.h> + + .text + .align 3 + .type __syscall_common,@function + .ent __syscall_common, 0 + .globl __syscall_common +__syscall_common: + .frame sp,0,ra,0 + callsys + beq a3, 1f + br pv, 2f # pv <- pc +2: + ldgp gp, 0(pv) + lda a1, errno + stl v0, 0(a1) + lda v0, -1(zero) +1: + ret zero,(ra),1 + + .size __syscall_common,.-__syscall_common + .end __syscall_common diff --git a/usr/klibc/arch/alpha/sysdual.S b/usr/klibc/arch/alpha/sysdual.S new file mode 100644 index 0000000..03e719d --- /dev/null +++ b/usr/klibc/arch/alpha/sysdual.S @@ -0,0 +1,33 @@ +# +# arch/alpha/sysdual.S +# + +# +# Some system calls have an alternate return value in r20 (a4). +# This system call stub is for system calls where that is +# the "real" return value. +# + +#include <machine/asm.h> + + .text + .align 3 + .type __syscall_dual1,@function + .ent __syscall_dual1, 0 + .globl __syscall_dual1 +__syscall_dual1: + .frame sp,0,ra,0 + callsys + beq a3, 1f + br pv, 2f # pv <- pc +2: + ldgp gp, 0(pv) + lda a1, errno + stl v0, 0(a1) + lda a4, -1(zero) +1: + mov a4, v0 + ret zero,(ra),1 + + .size __syscall_dual1,.-__syscall_dual1 + .end __syscall_dual1 diff --git a/usr/klibc/arch/alpha/sysstub.ph b/usr/klibc/arch/alpha/sysstub.ph new file mode 100644 index 0000000..08b97e8 --- /dev/null +++ b/usr/klibc/arch/alpha/sysstub.ph @@ -0,0 +1,37 @@ +# -*- perl -*- +# +# arch/alpha/sysstub.ph +# +# Script to generate system call stubs +# + +# On Alpha, most system calls follow the standard convention, with the +# system call number in r0 (v0), return an error value in r19 (a3) as +# well as the return value in r0 (v0). +# +# A few system calls are dual-return with the second return value in +# r20 (a4). + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + $stype = $stype || 'common'; + $stype = 'common' if ( $stype eq 'dual0' ); + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "#include <machine/asm.h>\n"; + print OUT "\n"; + print OUT "\t.text\n"; + print OUT "\t.type ${fname},\@function\n"; + print OUT "\t.ent\t${fname}, 0\n"; # What is this? + print OUT "\t.globl ${fname}\n"; + print OUT "${fname}:\n"; + print OUT "\tlda\tv0, __NR_${sname}(zero)\n"; + print OUT "\tbr __syscall_${stype}\n"; + print OUT "\t.size\t${fname},.-${fname}\n"; + print OUT "\t.end\t${fname}\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/arm/Kbuild b/usr/klibc/arch/arm/Kbuild new file mode 100644 index 0000000..4946b6b --- /dev/null +++ b/usr/klibc/arch/arm/Kbuild @@ -0,0 +1,13 @@ +# +# klibc files for arm +# + +klib-y := setjmp.o syscall.o vfork.o aeabi_nonsense.o + +klib-y += ../../libgcc/__udivmodsi4.o ../../libgcc/__divdi3.o +klib-y += ../../libgcc/__moddi3.o ../../libgcc/__udivdi3.o +klib-y += ../../libgcc/__umoddi3.o ../../libgcc/__udivmoddi4.o +klib-y += ../../libgcc/__clzsi2.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/arm/MCONFIG b/usr/klibc/arch/arm/MCONFIG new file mode 100644 index 0000000..db5b14a --- /dev/null +++ b/usr/klibc/arch/arm/MCONFIG @@ -0,0 +1,40 @@ +# -*- makefile -*- +# +# arch/arm/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +CPU_ARCH ?= armv4 +CPU_TUNE ?= strongarm + +KLIBCOPTFLAGS += -Os -march=$(CPU_ARCH) -mtune=$(CPU_TUNE) +KLIBCBITSIZE = 32 +KLIBCREQFLAGS += -fno-exceptions +KLIBCSTRIPFLAGS += -R .ARM.exidx + +ifeq ($(CONFIG_KLIBC_THUMB),y) +CPU_ARCH := $(CPU_ARCH)t +KLIBCREQFLAGS += -mthumb +KLIBCREQFLAGS += -mabi=aapcs-linux +else +# Extra linkflags when building the shared version of the library +ifeq ($(CONFIG_AEABI),y) +KLIBCREQFLAGS += -mabi=aapcs-linux -mno-thumb-interwork +else +KLIBCREQFLAGS += -mabi=apcs-gnu -mno-thumb-interwork +endif +endif + +# Normal binaries start at 64 kiB. A32 branch instructions have a +# range of ±32 MiB and T32 branch instructions only ±16 MiB, so we +# have to put klibc.so in that range. Putting it close above the +# executable can cause breakage, so instead swap them around: +# klibc.so at 2 MiB and executable at 4 MiB. +KLIBCLDFLAGS = $(LD_IMAGE_BASE_OPT) 0x400000 +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x200000 + +# Kernel uses dedicated page or vDSO for signal return since 2.6.13 +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/arm/__muldi3.c b/usr/klibc/arch/arm/__muldi3.c new file mode 100644 index 0000000..3fdeb2b --- /dev/null +++ b/usr/klibc/arch/arm/__muldi3.c @@ -0,0 +1,15 @@ +#include <stdint.h> + +uint64_t __muldi3(uint64_t a, uint64_t b) +{ + uint32_t al = (uint32_t)a; + uint32_t ah = (uint32_t)(a >> 32); + uint32_t bl = (uint32_t)b; + uint32_t bh = (uint32_t)(b >> 32); + uint64_t v; + + v = (uint64_t)al * bl; + v += (uint64_t)(al*bh+ah*bl) << 32; + + return v; +} diff --git a/usr/klibc/arch/arm/aeabi_nonsense.S b/usr/klibc/arch/arm/aeabi_nonsense.S new file mode 100644 index 0000000..c69eb11 --- /dev/null +++ b/usr/klibc/arch/arm/aeabi_nonsense.S @@ -0,0 +1,9 @@ + .text + .globl __aeabi_unwind_cpp_pr0 +__aeabi_unwind_cpp_pr0: + .globl __aeabi_unwind_cpp_pr1 +__aeabi_unwind_cpp_pr1: + .globl __aeabi_unwind_cpp_pr2 +__aeabi_unwind_cpp_pr2: + .globl __aeabi_unwind_cpp_pr3 +__aeabi_unwind_cpp_pr3: diff --git a/usr/klibc/arch/arm/crt0.S b/usr/klibc/arch/arm/crt0.S new file mode 100644 index 0000000..1e81f8e --- /dev/null +++ b/usr/klibc/arch/arm/crt0.S @@ -0,0 +1,23 @@ +# +# arch/arm/crt0.S +# +# void _start(void) +# { +# __libc_init(elf_structure, atexit_ptr); +# } +# + + .text + .balign 4 + .type _start,#function + .globl _start + +#ifdef __thumb__ + .thumb_func +#endif + +_start: mov r0, sp + mov r1, #0 + bl __libc_init + + .size _start,.-_start diff --git a/usr/klibc/arch/arm/setjmp.S b/usr/klibc/arch/arm/setjmp.S new file mode 100644 index 0000000..9f96274 --- /dev/null +++ b/usr/klibc/arch/arm/setjmp.S @@ -0,0 +1,108 @@ +# +# arch/arm/setjmp.S +# +# setjmp/longjmp for the ARM architecture +# + +#include <klibc/asmmacros.h> + +#ifndef __thumb__ + +# +# "Pure ARM" version +# +# The jmp_buf is assumed to contain the following, in order: +# r4 +# r5 +# r6 +# r7 +# r8 +# r9 +# r10 +# fp +# sp +# lr +# + + .text + .balign 4 + .globl setjmp + .type setjmp, #function +setjmp: + stmia r0, {r4, r5, r6, r7, r8, r9, r10, fp, sp, lr} + mov r0, #0 + BX(lr) + .size setjmp,.-setjmp + + .text + .balign 4 + .globl longjmp + .type longjmp, #function +longjmp: + ldmia r0, {r4, r5, r6, r7, r8, r9, r10, fp, sp, lr} + movs r0, r1 + moveq r0, #1 + BX(lr) + .size longjmp,.-longjmp + +#else /* __thumb__ */ + +# +# Thumb version +# +# The jmp_buf is assumed to contain the following, in order: +# lr +# r4 +# r5 +# r6 +# r7 +# r8 +# r9 +# r10 +# fp +# sp +# + + .text + .balign 4 + .globl setjmp + .type setjmp, #function + .thumb_func +setjmp: + mov r2, r0 + mov r3, lr + stmia r0!, {r3, r4, r5, r6, r7} + mov r3, r8 + mov r4, r9 + mov r5, r10 + mov r6, fp + mov r7, sp + stmia r0!, {r3, r4, r5, r6, r7} + /* Do not trash r4 .. r7 */ + ldmia r2!, {r3, r4, r5, r6, r7} + mov r0, #0 + BX(lr) + .size setjmp,.-setjmp + + .text + .balign 4 + .globl longjmp + .type longjmp, #function + .thumb_func +longjmp: + mov r2, r0 + add r0, #5*4 + ldmia r0!, {r3, r4, r5, r6, r7} + mov r8, r3 + mov r9, r4 + mov r10, r5 + mov fp, r6 + mov sp, r7 + ldmia r2!, {r3, r4, r5, r6, r7} + mov r0, r1 + bne 1f + mov r0, #1 +1: BX(r3) + .size longjmp,.-longjmp + +#endif /* __thumb__ */ diff --git a/usr/klibc/arch/arm/syscall.S b/usr/klibc/arch/arm/syscall.S new file mode 100644 index 0000000..e5b04e2 --- /dev/null +++ b/usr/klibc/arch/arm/syscall.S @@ -0,0 +1,65 @@ +/* + * arch/arm/syscall.S + * + * System call common handling + */ + + .type __syscall_common,#function + .globl __syscall_common +#ifndef __thumb__ + /* ARM version - this is executed after the swi, unless + we are compiled in EABI mode */ + + .balign 4 +__syscall_common: +#ifdef __ARM_EABI__ + ldr r4, [sp,#16] + ldr r5, [sp,#20] + ldr r7, [lr] + swi 0 +#endif + cmn r0, #4096 + rsbcs r2, r0, #0 + ldrcs r3, 1f + mvncs r0, #0 + strcs r2, [r3] +#ifdef __ARM_EABI__ + ldmfd sp!,{r4,r5,r7,pc} +#else + ldmfd sp!,{r4,r5,pc} +#endif + + .balign 4 +1: + .word errno + +#else + /* Thumb version - must still load r4 and r5 and run swi */ + + .thumb_func + .balign 2 +__syscall_common: + mov r7, lr + ldr r4, [sp,#16] + sub r7, #1 /* Remove the Thumb bit */ + ldr r5, [sp,#20] + ldrh r7, [r7] + swi 0 + ldr r1, 2f + cmp r0, r1 + bcc 1f + ldr r1, 3f + neg r2, r0 + mov r0, #1 + str r2, [r1] + neg r0, r0 +1: + pop {r4,r5,r7,pc} + + .balign 4 +2: + .word -4095 +3: + .word errno + +#endif diff --git a/usr/klibc/arch/arm/sysstub.ph b/usr/klibc/arch/arm/sysstub.ph new file mode 100644 index 0000000..1a4eca0 --- /dev/null +++ b/usr/klibc/arch/arm/sysstub.ph @@ -0,0 +1,58 @@ +# -*- perl -*- +# +# arch/arm/sysstub.ph +# +# Script to generate system call stubs +# + + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "#include <klibc/asmmacros.h>\n"; + + print OUT " .text\n"; + print OUT " .type ${fname}, #function\n"; + print OUT " .globl ${fname}\n"; + + print OUT "#ifndef __thumb__\n"; + + print OUT "#ifndef __ARM_EABI__\n"; + + # ARM version first + print OUT " .balign 4\n"; + print OUT "${fname}:\n"; + print OUT " stmfd sp!,{r4,r5,lr}\n"; + print OUT " ldr r4,[sp,#12]\n"; + print OUT " ldr r5,[sp,#16]\n"; + print OUT " swi # __NR_${sname}\n"; + print OUT " b __syscall_common\n"; + + print OUT "#else /* __ARM_EABI__ */\n"; + + # ARM EABI version + print out " .balign 4\n"; + print OUT "${fname}:\n"; + print OUT " stmfd sp!,{r4,r5,r7,lr}\n"; + print OUT " bl __syscall_common\n"; + print OUT " .word __NR_${sname}\n"; + + print OUT "#endif /* __ARM_EABI__ */\n"; + print OUT "#else /* __thumb__ */\n"; + + # Thumb version + print OUT " .balign 8\n"; + print OUT " .thumb_func\n"; + print OUT "${fname}:\n"; + print OUT " push {r4,r5,r7,lr}\n"; + print OUT " bl __syscall_common\n"; + print OUT " .short __NR_${sname}\n"; + + print OUT "#endif /* __thumb__*/\n"; + + print OUT " .size ${fname},.-${fname}\n"; +} + +1; diff --git a/usr/klibc/arch/arm/vfork.S b/usr/klibc/arch/arm/vfork.S new file mode 100644 index 0000000..7130b65 --- /dev/null +++ b/usr/klibc/arch/arm/vfork.S @@ -0,0 +1,61 @@ +/* + * arch/arm/vfork.S + * + * vfork - nasty system call which must not use the stack. + */ + +#include <klibc/asmmacros.h> +#include <asm/unistd.h> + + .type vfork,#function + .globl vfork +#ifndef __thumb__ + + .balign 4 +vfork: +#ifdef __ARM_EABI__ + mov r3, r7 + mov r7, # __NR_vfork + swi 0 + mov r7, r3 +#else + swi # __NR_vfork +#endif + cmn r0, #4096 + rsbcs r2, r0, #0 + ldrcs r3, 1f + mvncs r0, #0 + strcs r2, [r3] + BX(lr) + + .balign 4 +1: + .word errno + +#else + + .thumb_func + .balign 2 +vfork: + mov r3, r7 + mov r7, # __NR_vfork + swi 0 + mov r7, r3 + ldr r1, 2f + cmp r0, r1 + bcc 1f + ldr r1, 3f + neg r2, r0 + mov r0, #1 + str r2, [r1] + neg r0, r0 +1: + BX(lr) + + .balign 4 +2: + .word -4095 +3: + .word errno + +#endif diff --git a/usr/klibc/arch/arm64/Kbuild b/usr/klibc/arch/arm64/Kbuild new file mode 100644 index 0000000..f8643b5 --- /dev/null +++ b/usr/klibc/arch/arm64/Kbuild @@ -0,0 +1,7 @@ + +# klibc files for arm64 +# + +klib-y := setjmp.o syscall.o vfork.o +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/arm64/MCONFIG b/usr/klibc/arch/arm64/MCONFIG new file mode 100644 index 0000000..b90568e --- /dev/null +++ b/usr/klibc/arch/arm64/MCONFIG @@ -0,0 +1,27 @@ +# -*- makefile -*- +# +# arch/arm64/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +CPU_ARCH ?= armv8-a +CPU_TUNE ?= generic + +KLIBCOPTFLAGS += -g -Os -march=$(CPU_ARCH) -mtune=$(CPU_TUNE) +KLIBCBITSIZE = 64 +KLIBCREQFLAGS += -fno-exceptions -mgeneral-regs-only +KLIBCLDFLAGS = $(LD_IMAGE_BASE_OPT) 0x00400000 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture + +# On arm64, binaries are normally loaded at 4MB. Place klibc.so +# a little before that at 2MB to prevent overlap. +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x0200000 + +# Kernel has never used stack trampolines +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/arm64/crt0.S b/usr/klibc/arch/arm64/crt0.S new file mode 100644 index 0000000..0b2dd32 --- /dev/null +++ b/usr/klibc/arch/arm64/crt0.S @@ -0,0 +1,19 @@ +# +# arch/arm64/crt0.S +# +# void _start(void) +# { +# __libc_init(elf_structure, atexit_ptr); +# } +# + + .text + .balign 8 + .type _start,#function + .globl _start + +_start: + mov x0, sp + mov x1, #0 + bl __libc_init + .size _start,.-_start diff --git a/usr/klibc/arch/arm64/setjmp.S b/usr/klibc/arch/arm64/setjmp.S new file mode 100644 index 0000000..284e328 --- /dev/null +++ b/usr/klibc/arch/arm64/setjmp.S @@ -0,0 +1,45 @@ +# +# arch/arm64/setjmp.S +# +# setjmp/longjmp for arm64 +# + +# we specify -mgeneral-regs-only as a build flag thus do not need to +# save d8-d15 + + .text + .balign 8 + .globl setjmp + .type setjmp, #function +setjmp: + mov x1, sp + stp x19, x20, [x0, #0] + stp x21, x22, [x0, #16] + stp x23, x24, [x0, #32] + stp x25, x26, [x0, #48] + stp x27, x28, [x0, #64] + stp x29, x30, [x0, #80] + str x1, [x0, #96] + mov x0, #0 /* set the return value of setjmp */ + br x30 + .size setjmp,.-setjmp + + .text + .balign 8 + .globl longjmp + .type longjmp, #function +longjmp: + ldp x19, x20, [x0, #0] + ldp x21, x22, [x0, #16] + ldp x23, x24, [x0, #32] + ldp x25, x26, [x0, #48] + ldp x27, x28, [x0, #64] + ldp x29, x30, [x0, #80] + ldr x2, [x0, #96] + mov sp, x2 + mov x0, x1 + cbnz x1, 1f + mov x0, #1 +1: + br x30 + .size longjmp,.-longjmp diff --git a/usr/klibc/arch/arm64/syscall.S b/usr/klibc/arch/arm64/syscall.S new file mode 100644 index 0000000..e100412 --- /dev/null +++ b/usr/klibc/arch/arm64/syscall.S @@ -0,0 +1,25 @@ +/* + * arch/arm64/syscall.S + * + * System call common handling - if the return + * value from the system call is negative, then + * extract the magnitude and return it as errno and + * return -1, if the return value is 0 that is + * success case. + */ + + .type __syscall_common,#function + .globl __syscall_common + .balign 8 + +__syscall_common: + cmp x0, #0x0 + b.ge 2f + neg x0, x0 + ldr x8, 1f + str w0, [x8] + mov x0, #-1 +2: + ret +1: + .dword errno diff --git a/usr/klibc/arch/arm64/sysstub.ph b/usr/klibc/arch/arm64/sysstub.ph new file mode 100644 index 0000000..095b1e8 --- /dev/null +++ b/usr/klibc/arch/arm64/sysstub.ph @@ -0,0 +1,24 @@ +# -*- perl -*- +# +# arch/arm64/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT " .text\n"; + print OUT " .type ${fname}, #function\n"; + print OUT " .globl ${fname}\n"; + print OUT " .balign 8\n"; + print OUT "${fname}:\n"; + print OUT " mov w8,__NR_${sname}\n"; + print OUT " svc 0\n"; + print OUT " b __syscall_common\n"; + print OUT " .size ${fname},.-${fname}\n"; +} + +1; diff --git a/usr/klibc/arch/arm64/vfork.S b/usr/klibc/arch/arm64/vfork.S new file mode 100644 index 0000000..c30b2a0 --- /dev/null +++ b/usr/klibc/arch/arm64/vfork.S @@ -0,0 +1,33 @@ +/* + * arch/arm64/vfork.S + * + * vfork - a system call which must not use the stack. + */ + +#include <asm/unistd.h> + + .type vfork,#function + .globl vfork + .balign 8 + +vfork: + /* Prepare for the system call */ + /* 1. Push the function pointer and argument location + on to the child process stack */ + /* 2. Gather the Flags */ + /* New sp is already in x1. */ + mov x0, #0x4111 /* CLONE_VM | CLONE_VFORK | SIGCHLD */ + mov x1, sp + mov w8,__NR_clone + svc 0 + cmp x0, #0x0 + b.ge 2f + neg x0, x0 + ldr x8, 1f + str x0, [x8] + mov x0, #-1 +2: + ret +1: + .dword errno + .size vfork,.-vfork diff --git a/usr/klibc/arch/i386/Kbuild b/usr/klibc/arch/i386/Kbuild new file mode 100644 index 0000000..de237be --- /dev/null +++ b/usr/klibc/arch/i386/Kbuild @@ -0,0 +1,17 @@ +# +# klibc .o files for i386 +# + +klib-y := setjmp.o syscall.o varsyscall.o +klib-y += open.o openat.o vfork.o +klib-y += libgcc/__ashldi3.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o +klib-y += libgcc/__muldi3.o libgcc/__negdi2.o + +klib-y += ../../libgcc/__divdi3.o +klib-y += ../../libgcc/__moddi3.o +klib-y += ../../libgcc/__udivdi3.o +klib-y += ../../libgcc/__umoddi3.o +klib-y += ../../libgcc/__udivmoddi4.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/i386/MCONFIG b/usr/klibc/arch/i386/MCONFIG new file mode 100644 index 0000000..e6f50dd --- /dev/null +++ b/usr/klibc/arch/i386/MCONFIG @@ -0,0 +1,36 @@ +# -*- makefile -*- +# +# arch/i386/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +# Enable this to compile with register parameters; only safe for +# gcc >= 3 + +ifeq ($(CONFIG_REGPARM),y) +REGPARM_OPT := -mregparm=3 -D_REGPARM=3 +endif + +gcc_align_option := $(call cc-option, \ + -falign-functions=0 -falign-jumps=0 -falign-loops=0, \ + -malign-functions=0 -malign-jumps=0 -malign-loops=0) +gcc_m32_option := $(call cc-option, -m32, ) + +KLIBCOPTFLAGS += -march=i386 -Os -g -fomit-frame-pointer $(gcc_align_option) +KLIBCLDFLAGS = -m elf_i386 +KLIBCREQFLAGS += $(REGPARM_OPT) +KLIBCARCHREQFLAGS += $(gcc_m32_option) + +KLIBCBITSIZE = 32 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 96 MB - normal binaries start at 128 MB +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x06000000 + +# Kernel uses dedicated page or vDSO for signal return since 2.5.55 +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/i386/crt0.S b/usr/klibc/arch/i386/crt0.S new file mode 100644 index 0000000..8c6635e --- /dev/null +++ b/usr/klibc/arch/i386/crt0.S @@ -0,0 +1,31 @@ +# +# arch/i386/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + + .text + .align 4 + .type _start,@function + .globl _start +_start: + # Save the address of the ELF argument array + movl %esp,%eax # Address of ELF arguments + # Set up a faux stack frame for the benefit of gdb + xorl %ebp,%ebp + push %ebp # Keep gdb from getting confused + push %ebp # Keep gdb from getting confused + # Push the arguments and called __libc_init() +#ifndef _REGPARM + push %edx # atexit() function + push %eax # ELF array +#endif + call __libc_init + # If __libc_init returns, problem... + hlt + + .size _start, .-_start diff --git a/usr/klibc/arch/i386/libgcc/__ashldi3.S b/usr/klibc/arch/i386/libgcc/__ashldi3.S new file mode 100644 index 0000000..7344142 --- /dev/null +++ b/usr/klibc/arch/i386/libgcc/__ashldi3.S @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__ashldi3.S + * + * 64-bit shl + */ + .text + .align 4 + .globl __ashldi3 + .type __ashldi3,@function +__ashldi3: +#ifndef _REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx + movb 12(%esp),%cl +#endif + cmpb $32,%cl + jae 1f + + shldl %cl,%eax,%edx + shl %cl,%eax + ret + +1: + xorl %edx,%edx + shl %cl,%eax + xchgl %edx,%eax + ret + + .size __ashldi3,.-__ashldi3 diff --git a/usr/klibc/arch/i386/libgcc/__ashrdi3.S b/usr/klibc/arch/i386/libgcc/__ashrdi3.S new file mode 100644 index 0000000..7666eb2 --- /dev/null +++ b/usr/klibc/arch/i386/libgcc/__ashrdi3.S @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__ashrdi3.S + * + * 64-bit sar + */ + .text + .align 4 + .globl __ashrdi3 + .type __ashrdi3,@function +__ashrdi3: +#ifndef _REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx + movb 12(%esp),%cl +#endif + cmpb $32,%cl + jae 1f + + shrdl %cl,%edx,%eax + sarl %cl,%edx + ret + +1: + sarl %cl,%edx + movl %edx,%eax + cdq + ret + + .size __ashrdi3,.-__ashrdi3 diff --git a/usr/klibc/arch/i386/libgcc/__lshrdi3.S b/usr/klibc/arch/i386/libgcc/__lshrdi3.S new file mode 100644 index 0000000..6a63c52 --- /dev/null +++ b/usr/klibc/arch/i386/libgcc/__lshrdi3.S @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__lshrdi3.S + * + * 64-bit shr + */ + .text + .align 4 + .globl __lshrdi3 + .type __lshrdi3,@function +__lshrdi3: +#ifndef _REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx + movb 12(%esp),%cl +#endif + cmpb $32,%cl + jae 1f + + shrdl %cl,%edx,%eax + shrl %cl,%edx + ret + +1: + shrl %cl,%edx + xorl %eax,%eax + xchgl %edx,%eax + ret + + .size __lshrdi3,.-__lshrdi3 diff --git a/usr/klibc/arch/i386/libgcc/__muldi3.S b/usr/klibc/arch/i386/libgcc/__muldi3.S new file mode 100644 index 0000000..472c7cc --- /dev/null +++ b/usr/klibc/arch/i386/libgcc/__muldi3.S @@ -0,0 +1,34 @@ +/* + * arch/i386/libgcc/__muldi3.S + * + * 64*64 = 64 bit unsigned multiplication + */ + + .text + .align 4 + .globl __muldi3 + .type __muldi3,@function +__muldi3: + push %esi +#ifndef _REGPARM + movl 8(%esp),%eax + movl %eax,%esi + movl 16(%esp),%ecx + mull %ecx + imull 12(%esp),%ecx + imull 20(%esp),%esi + addl %ecx,%edx + addl %esi,%edx +#else + movl %eax,%esi + push %edx + mull %ecx + imull 8(%esp),%esi + addl %esi,%edx + pop %esi + imull %esi,%ecx + addl %ecx,%edx +#endif + pop %esi + ret + .size __muldi3,.-__muldi3 diff --git a/usr/klibc/arch/i386/libgcc/__negdi2.S b/usr/klibc/arch/i386/libgcc/__negdi2.S new file mode 100644 index 0000000..147ad94 --- /dev/null +++ b/usr/klibc/arch/i386/libgcc/__negdi2.S @@ -0,0 +1,21 @@ +/* + * arch/i386/libgcc/__negdi2.S + * + * 64-bit negation + */ + + .text + .align 4 + .globl __negdi2 + .type __negdi2,@function +__negdi2: +#ifndef _REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx +#endif + negl %edx + negl %eax + sbbl $0,%edx + ret + + .size __negdi2,.-__negdi2 diff --git a/usr/klibc/arch/i386/open.S b/usr/klibc/arch/i386/open.S new file mode 100644 index 0000000..7cd136c --- /dev/null +++ b/usr/klibc/arch/i386/open.S @@ -0,0 +1,29 @@ +/* + * arch/i386/open.S + * + * Handle the open() system call - oddball due to the varadic + * prototype, which forces the use of the cdecl calling convention, + * and the need for O_LARGEFILE. + */ + +#include <asm/unistd.h> + +/* <asm/fcntl.h>, despite the name, isn't assembly-safe */ +#define O_LARGEFILE 0100000 + + .globl open + .type open,@function + +open: +#ifdef _REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx + movl 12(%esp),%ecx + orl $O_LARGEFILE,%edx +#else + orl $O_LARGEFILE,8(%esp) +#endif + pushl $__NR_open + jmp __syscall_common + + .size open,.-open diff --git a/usr/klibc/arch/i386/openat.S b/usr/klibc/arch/i386/openat.S new file mode 100644 index 0000000..2dfdfe2 --- /dev/null +++ b/usr/klibc/arch/i386/openat.S @@ -0,0 +1,26 @@ +/* + * arch/i386/openat.S + * + * Handle the openat() system call - oddball due to the varadic + * prototype, which forces the use of the cdecl calling convention, + * and the need for O_LARGEFILE. + */ + +#include <asm/unistd.h> + +/* <asm/fcntl.h>, despite the name, isn't assembly-safe */ +#define O_LARGEFILE 0100000 + +#ifdef __NR_openat /* Don't build if kernel headers too old */ + + .globl openat + .type openat,@function + +openat: + orl $O_LARGEFILE,12(%esp) + pushl $__NR_openat + jmp __syscall_varadic + + .size openat,.-openat + +#endif diff --git a/usr/klibc/arch/i386/setjmp.S b/usr/klibc/arch/i386/setjmp.S new file mode 100644 index 0000000..b766792 --- /dev/null +++ b/usr/klibc/arch/i386/setjmp.S @@ -0,0 +1,58 @@ +# +# arch/i386/setjmp.S +# +# setjmp/longjmp for the i386 architecture +# + +# +# The jmp_buf is assumed to contain the following, in order: +# %ebx +# %esp +# %ebp +# %esi +# %edi +# <return address> +# + + .text + .align 4 + .globl setjmp + .type setjmp, @function +setjmp: +#ifdef _REGPARM + movl %eax,%edx +#else + movl 4(%esp),%edx +#endif + popl %ecx # Return address, and adjust the stack + xorl %eax,%eax # Return value + movl %ebx,(%edx) + movl %esp,4(%edx) # Post-return %esp! + pushl %ecx # Make the call/return stack happy + movl %ebp,8(%edx) + movl %esi,12(%edx) + movl %edi,16(%edx) + movl %ecx,20(%edx) # Return address + ret + + .size setjmp,.-setjmp + + .text + .align 4 + .globl longjmp + .type longjmp, @function +longjmp: +#ifdef _REGPARM + xchgl %eax,%edx +#else + movl 4(%esp),%edx # jmp_ptr address + movl 8(%esp),%eax # Return value +#endif + movl (%edx),%ebx + movl 4(%edx),%esp + movl 8(%edx),%ebp + movl 12(%edx),%esi + movl 16(%edx),%edi + jmp *20(%edx) + + .size longjmp,.-longjmp diff --git a/usr/klibc/arch/i386/syscall.S b/usr/klibc/arch/i386/syscall.S new file mode 100644 index 0000000..b7814e8 --- /dev/null +++ b/usr/klibc/arch/i386/syscall.S @@ -0,0 +1,82 @@ +/* + * arch/i386/syscall.S + * + * Common tail-handling code for system calls. + * + * The arguments are on the stack; the system call number in %eax. + */ + +#define ARG(n) (4*(n)+24)(%esp) +#define SYSNO ARG(-2) + + .text + .align 4 + .globl __syscall_common + .type __syscall_common,@function +__syscall_common: + pushl %ebx + pushl %esi + pushl %edi + pushl %ebp + +#ifdef _REGPARM + xchgl %ecx,%edx + movl %eax,%ebx + movl SYSNO,%eax + movl ARG(0),%esi + movl ARG(1),%edi + movl ARG(2),%ebp +#else + movl SYSNO,%eax + movl ARG(0),%ebx # Syscall arguments + movl ARG(1),%ecx + movl ARG(2),%edx + movl ARG(3),%esi + movl ARG(4),%edi + movl ARG(5),%ebp +#endif + .globl __syscall_common_tail +__syscall_common_tail: + call *__syscall_entry + + cmpl $-4095,%eax + + popl %ebp + popl %edi + popl %esi + popl %ebx + popl %edx # Drop system call number + + jb 1f + + # Error return, must set errno + negl %eax + movl %eax,errno + orl $-1,%eax # Return -1 + +1: + ret + + .size __syscall_common,.-__syscall_common + +#ifndef _REGPARM + + .globl __syscall_varadic + .type __syscall_varadic,@function +__syscall_varadic = __syscall_common + +#endif + + .align 4 +__syscall_int80: + int $0x80 + ret + .type __syscall_int80,@function + .size __syscall_int80,.-__syscall_int80 + + .data + .align 4 + .globl __syscall_entry +__syscall_entry: + .long __syscall_int80 + .size __syscall_entry,.-__syscall_entry diff --git a/usr/klibc/arch/i386/sysstub.ph b/usr/klibc/arch/i386/sysstub.ph new file mode 100644 index 0000000..e73a3ff --- /dev/null +++ b/usr/klibc/arch/i386/sysstub.ph @@ -0,0 +1,26 @@ +# -*- perl -*- +# +# arch/i386/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.type ${fname},\@function\n"; + print OUT "\t.globl ${fname}\n"; + print OUT "${fname}:\n"; + + $stype = 'common' if ( $stype eq '' ); + + print OUT "\tpushl \$__NR_${sname}\n"; + print OUT "\tjmp __syscall_$stype\n"; + print OUT "\t.size ${fname},.-${fname}\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/i386/varsyscall.S b/usr/klibc/arch/i386/varsyscall.S new file mode 100644 index 0000000..e796a93 --- /dev/null +++ b/usr/klibc/arch/i386/varsyscall.S @@ -0,0 +1,37 @@ +/* + * arch/i386/varsyscall.S + * + * Common tail-handling code for varadic system calls (which always + * use the cdecl convention.) + * + * The arguments are on the stack; the system call number in %eax. + */ + +#ifdef _REGPARM + +#define ARG(n) (4*n+24)(%esp) +#define SYSNO ARG(-2) + + .text + .align 4 + .globl __syscall_varadic + .type __syscall_varadic,@function +__syscall_varadic: + pushl %ebx + pushl %esi + pushl %edi + pushl %ebp + + movl SYSNO,%eax + movl ARG(0),%ebx # Syscall arguments + movl ARG(1),%ecx + movl ARG(2),%edx + movl ARG(3),%esi + movl ARG(4),%edi + movl ARG(5),%ebp + + jmp __syscall_common_tail + + .size __syscall_varadic,.-__syscall_varadic + +#endif diff --git a/usr/klibc/arch/i386/vfork.S b/usr/klibc/arch/i386/vfork.S new file mode 100644 index 0000000..d32b9b9 --- /dev/null +++ b/usr/klibc/arch/i386/vfork.S @@ -0,0 +1,29 @@ +# +# usr/klibc/arch/i386/vfork.S +# +# vfork is nasty - there must be nothing at all on the stack above +# the stack frame of the enclosing function. +# +# We *MUST* use int $0x80, because calling the vdso would screw up +# our stack handling. +# + +#include <asm/unistd.h> + + .text + .align 4 + .globl vfork + .type vfork, @function +vfork: + popl %edx /* Return address */ + movl $__NR_vfork, %eax + int $0x80 /* DO NOT call the vdso here! */ + pushl %edx + cmpl $-4095, %eax + jae 1f + ret +1: + negl %eax + movl %eax, errno + orl $-1, %eax + ret diff --git a/usr/klibc/arch/ia64/Kbuild b/usr/klibc/arch/ia64/Kbuild new file mode 100644 index 0000000..41b8ca0 --- /dev/null +++ b/usr/klibc/arch/ia64/Kbuild @@ -0,0 +1,13 @@ +# +# klibc files for ia64 +# + +klib-y := vfork.o setjmp.o sigaction.o pipe.o syscall.o + +klib-y += ../../libgcc/__divdi3.o ../../libgcc/__divsi3.o +klib-y += ../../libgcc/__udivdi3.o ../../libgcc/__udivsi3.o +klib-y += ../../libgcc/__umodsi3.o ../../libgcc/__umoddi3.o +klib-y += ../../libgcc/__udivmodsi4.o ../../libgcc/__udivmoddi4.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/ia64/MCONFIG b/usr/klibc/arch/ia64/MCONFIG new file mode 100644 index 0000000..db6a4ce --- /dev/null +++ b/usr/klibc/arch/ia64/MCONFIG @@ -0,0 +1,17 @@ +# -*- makefile -*- +# +# arch/ia64/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCARCHREQFLAGS = -mno-pic +KLIBCOPTFLAGS += -Os +KLIBCBITSIZE = 64 + +KLIBCSHAREDFLAGS = -T $(srctree)/$(src)/arch/$(KLIBCARCH)/klibc.ld + +# Kernel has never used stack trampolines +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/ia64/crt0.S b/usr/klibc/arch/ia64/crt0.S new file mode 100644 index 0000000..2394cc4 --- /dev/null +++ b/usr/klibc/arch/ia64/crt0.S @@ -0,0 +1,23 @@ + +#include <asm/fpu.h> + + .align 32 + .global _start + + .proc _start + .type _start,@function +_start: + .prologue + .save rp, r0 + + alloc r2 = ar.pfs,0,0,2,0 + movl r3 = FPSR_DEFAULT + ;; + adds out0= 16,sp /* argc pointer */ + + .body + br.call.sptk.few rp = __libc_init + ;; + break 0 /* break miserably if we ever return */ + + .endp _start diff --git a/usr/klibc/arch/ia64/klibc.ld b/usr/klibc/arch/ia64/klibc.ld new file mode 100644 index 0000000..d203d0a --- /dev/null +++ b/usr/klibc/arch/ia64/klibc.ld @@ -0,0 +1,267 @@ +/* Linker script for klibc.so. Whilst we can control text-segment for klibc.so + to not collide, the data segment is not controllable and hard-coded to start + at 0x6000000000000000 for position-dependent outputs, rather than simply + following on from the text segment like position-independent output or other + architectures. */ +/* Copyright (C) 2014-2018 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + /* Dynamic linker is normally placed in the 0x20..0 segment along with any + mmap'ed shared libraries. */ + . = SEGMENT_START("text-segment", 0x2000000000000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.dyn : + { + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data.rel.ro .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) + *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) + *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) + *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + PROVIDE_HIDDEN (__rel_iplt_start = .); + *(.rel.iplt) + PROVIDE_HIDDEN (__rel_iplt_end = .); + } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) + *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) + *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) + *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + .rel.plt : + { + *(.rel.plt) + } + .rela.plt : + { + *(.rela.plt) + } + .rela.IA_64.pltoff : { *(.rela.IA_64.pltoff) } + .init : + { + KEEP (*(SORT_NONE(.init))) + } =0x00300000010070000002000001000400 + .plt : { *(.plt) } + .iplt : { *(.iplt) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x00300000010070000002000001000400 + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } =0x00300000010070000002000001000400 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : + { + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .opd : { *(.opd) } + .IA_64.unwind_info : { KEEP(*(.IA_64.unwind_info* .gnu.linkonce.ia64unwi.*)) } + .IA_64.unwind : { KEEP(*(.IA_64.unwind* .gnu.linkonce.ia64unw.*)) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table + .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges + .exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } + /* Thread Local Storage sections */ + .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } + .IA_64.pltoff : { *(.IA_64.pltoff) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .sbss : + { + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we don't + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} diff --git a/usr/klibc/arch/ia64/pipe.S b/usr/klibc/arch/ia64/pipe.S new file mode 100644 index 0000000..ae31a3a --- /dev/null +++ b/usr/klibc/arch/ia64/pipe.S @@ -0,0 +1,27 @@ +#include <asm/unistd.h> + .align 32 + .proc pipe + .global pipe +pipe: + alloc r16 = ar.pfs, 1, 0, 8, 0 + mov r33 = r32 + mov r15=__NR_pipe + ;; + break 0x100000 // Do the syscall + ;; + + cmp.ne p6, p7 = -1, r10 + mov r15 = r0 + ;; +(p6) st4 [r32] = r8, 4 + +(p7) movl r14 = errno +(p7) addl r15 = -1, r0 + ;; +(p6) st4 [r32] = r9 + +(p7) st4 [r14] = r8 + + mov r8 = r15 + br.ret.sptk.many b0 + .endp pipe diff --git a/usr/klibc/arch/ia64/setjmp.S b/usr/klibc/arch/ia64/setjmp.S new file mode 100644 index 0000000..ab1cea2 --- /dev/null +++ b/usr/klibc/arch/ia64/setjmp.S @@ -0,0 +1,343 @@ +/* + * IA-64 specific setjmp/longjmp routines + * + * Inspired by setjmp.s from the FreeBSD kernel. + */ + +#define J_UNAT 0 +#define J_NATS 0x8 +#define J_PFS 0x10 +#define J_BSP 0x18 +#define J_RNAT 0x20 +#define J_PREDS 0x28 +#define J_LC 0x30 +#define J_R4 0x38 +#define J_R5 0x40 +#define J_R6 0x48 +#define J_R7 0x50 +#define J_SP 0x58 +#define J_F2 0x60 +#define J_F3 0x70 +#define J_F4 0x80 +#define J_F5 0x90 +#define J_F16 0xa0 +#define J_F17 0xb0 +#define J_F18 0xc0 +#define J_F19 0xd0 +#define J_F20 0xe0 +#define J_F21 0xf0 +#define J_F22 0x100 +#define J_F23 0x110 +#define J_F24 0x120 +#define J_F25 0x130 +#define J_F26 0x140 +#define J_F27 0x150 +#define J_F28 0x160 +#define J_F29 0x170 +#define J_F30 0x180 +#define J_F31 0x190 +#define J_FPSR 0x1a0 +#define J_B0 0x1a8 +#define J_B1 0x1b0 +#define J_B2 0x1b8 +#define J_B3 0x1c0 +#define J_B4 0x1c8 +#define J_B5 0x1d0 +#define J_SIGMASK 0x1d8 +#define J_SIGSET 0x1e0 +#define J_GP 0x1f0 + +// int setjmp(struct jmp_buffer *) +// +// Setup a non-local goto. +// +// Description: +// +// SetJump stores the current register set in the area pointed to +// by "save". It returns zero. Subsequent calls to "LongJump" will +// restore the registers and return non-zero to the same location. +// +// On entry, r32 contains the pointer to the jmp_buffer +// + .align 32 + .global setjmp + .proc setjmp +setjmp: + // + // Make sure buffer is aligned at 16byte boundary + // + add r10 = -0x10,r0 ;; // mask the lower 4 bits + and r32 = r32, r10;; + add r32 = 0x10, r32;; // move to next 16 byte boundary + + add r10 = J_PREDS, r32 // skip Unats & pfs save area + add r11 = J_BSP, r32 + // + // save immediate context + // + mov r2 = ar.bsp // save backing store pointer + mov r3 = pr // save predicates + flushrs + ;; + // + // save user Unat register + // + mov r16 = ar.lc // save loop count register + mov r14 = ar.unat // save user Unat register + + st8 [r10] = r3, J_LC-J_PREDS + st8 [r11] = r2, J_R4-J_BSP + ;; + st8 [r10] = r16, J_R5-J_LC + st8 [r32] = r14, J_NATS // Note: Unat at the + // beginning of the save area + mov r15 = ar.pfs + ;; + // + // save preserved general registers & NaT's + // + st8.spill [r11] = r4, J_R6-J_R4 + ;; + st8.spill [r10] = r5, J_R7-J_R5 + ;; + st8.spill [r11] = r6, J_SP-J_R6 + ;; + st8.spill [r10] = r7, J_F3-J_R7 + ;; + st8.spill [r11] = sp, J_F2-J_SP + ;; + // + // save spilled Unat and pfs registers + // + mov r2 = ar.unat // save Unat register after spill + ;; + st8 [r32] = r2, J_PFS-J_NATS // save unat for spilled regs + ;; + st8 [r32] = r15 // save pfs + // + // save floating registers + // + stf.spill [r11] = f2, J_F4-J_F2 + stf.spill [r10] = f3, J_F5-J_F3 + ;; + stf.spill [r11] = f4, J_F16-J_F4 + stf.spill [r10] = f5, J_F17-J_F5 + ;; + stf.spill [r11] = f16, J_F18-J_F16 + stf.spill [r10] = f17, J_F19-J_F17 + ;; + stf.spill [r11] = f18, J_F20-J_F18 + stf.spill [r10] = f19, J_F21-J_F19 + ;; + stf.spill [r11] = f20, J_F22-J_F20 + stf.spill [r10] = f21, J_F23-J_F21 + ;; + stf.spill [r11] = f22, J_F24-J_F22 + stf.spill [r10] = f23, J_F25-J_F23 + ;; + stf.spill [r11] = f24, J_F26-J_F24 + stf.spill [r10] = f25, J_F27-J_F25 + ;; + stf.spill [r11] = f26, J_F28-J_F26 + stf.spill [r10] = f27, J_F29-J_F27 + ;; + stf.spill [r11] = f28, J_F30-J_F28 + stf.spill [r10] = f29, J_F31-J_F29 + ;; + stf.spill [r11] = f30, J_FPSR-J_F30 + stf.spill [r10] = f31, J_B0-J_F31 // size of f31 + fpsr + // + // save FPSR register & branch registers + // + mov r2 = ar.fpsr // save fpsr register + mov r3 = b0 + ;; + st8 [r11] = r2, J_B1-J_FPSR + st8 [r10] = r3, J_B2-J_B0 + mov r2 = b1 + mov r3 = b2 + ;; + st8 [r11] = r2, J_B3-J_B1 + st8 [r10] = r3, J_B4-J_B2 + mov r2 = b3 + mov r3 = b4 + ;; + st8 [r11] = r2, J_B5-J_B3 + st8 [r10] = r3 + mov r2 = b5 + ;; + st8 [r11] = r2 + ;; + // + // return + // + mov r8 = r0 // return 0 from setjmp + mov ar.unat = r14 // restore unat + br.ret.sptk b0 + .endp setjmp + +// +// void longjmp(struct jmp_buffer *, int val) +// +// Perform a non-local goto. +// +// Description: +// +// LongJump initializes the register set to the values saved by a +// previous 'SetJump' and jumps to the return location saved by that +// 'SetJump'. This has the effect of unwinding the stack and returning +// for a second time to the 'SetJump'. +// + + .align 32 + .global longjmp + .proc longjmp +longjmp: + // + // Make sure buffer is aligned at 16byte boundary + // + add r10 = -0x10,r0 ;; // mask the lower 4 bits + and r32 = r32, r10;; + add r32 = 0x10, r32;; // move to next 16 byte boundary + + // + // caching the return value as we do invala in the end + // + mov r8 = r33 // return value + + // + // get immediate context + // + mov r14 = ar.rsc // get user RSC conf + add r10 = J_PFS, r32 // get address of pfs + add r11 = J_NATS, r32 + ;; + ld8 r15 = [r10], J_BSP-J_PFS // get pfs + ld8 r2 = [r11], J_LC-J_NATS // get unat for spilled regs + ;; + mov ar.unat = r2 + ;; + ld8 r16 = [r10], J_PREDS-J_BSP // get backing store pointer + mov ar.rsc = r0 // put RSE in enforced lazy + mov ar.pfs = r15 + ;; + + // + // while returning from longjmp the BSPSTORE and BSP needs to be + // same and discard all the registers allocated after we did + // setjmp. Also, we need to generate the RNAT register since we + // did not flushed the RSE on setjmp. + // + mov r17 = ar.bspstore // get current BSPSTORE + ;; + cmp.ltu p6,p7 = r17, r16 // is it less than BSP of +(p6) br.spnt.few .flush_rse + mov r19 = ar.rnat // get current RNAT + ;; + loadrs // invalidate dirty regs + br.sptk.many .restore_rnat // restore RNAT + +.flush_rse: + flushrs + ;; + mov r19 = ar.rnat // get current RNAT + mov r17 = r16 // current BSPSTORE + ;; +.restore_rnat: + // + // check if RNAT is saved between saved BSP and curr BSPSTORE + // + mov r18 = 0x3f + ;; + dep r18 = r18,r16,3,6 // get RNAT address + ;; + cmp.ltu p8,p9 = r18, r17 // RNAT saved on RSE + ;; +(p8) ld8 r19 = [r18] // get RNAT from RSE + ;; + mov ar.bspstore = r16 // set new BSPSTORE + ;; + mov ar.rnat = r19 // restore RNAT + mov ar.rsc = r14 // restore RSC conf + + + ld8 r3 = [r11], J_R4-J_LC // get lc register + ld8 r2 = [r10], J_R5-J_PREDS // get predicates + ;; + mov pr = r2, -1 + mov ar.lc = r3 + // + // restore preserved general registers & NaT's + // + ld8.fill r4 = [r11], J_R6-J_R4 + ;; + ld8.fill r5 = [r10], J_R7-J_R5 + ld8.fill r6 = [r11], J_SP-J_R6 + ;; + ld8.fill r7 = [r10], J_F2-J_R7 + ld8.fill sp = [r11], J_F3-J_SP + ;; + // + // restore floating registers + // + ldf.fill f2 = [r10], J_F4-J_F2 + ldf.fill f3 = [r11], J_F5-J_F3 + ;; + ldf.fill f4 = [r10], J_F16-J_F4 + ldf.fill f5 = [r11], J_F17-J_F5 + ;; + ldf.fill f16 = [r10], J_F18-J_F16 + ldf.fill f17 = [r11], J_F19-J_F17 + ;; + ldf.fill f18 = [r10], J_F20-J_F18 + ldf.fill f19 = [r11], J_F21-J_F19 + ;; + ldf.fill f20 = [r10], J_F22-J_F20 + ldf.fill f21 = [r11], J_F23-J_F21 + ;; + ldf.fill f22 = [r10], J_F24-J_F22 + ldf.fill f23 = [r11], J_F25-J_F23 + ;; + ldf.fill f24 = [r10], J_F26-J_F24 + ldf.fill f25 = [r11], J_F27-J_F25 + ;; + ldf.fill f26 = [r10], J_F28-J_F26 + ldf.fill f27 = [r11], J_F29-J_F27 + ;; + ldf.fill f28 = [r10], J_F30-J_F28 + ldf.fill f29 = [r11], J_F31-J_F29 + ;; + ldf.fill f30 = [r10], J_FPSR-J_F30 + ldf.fill f31 = [r11], J_B0-J_F31 ;; + + // + // restore branch registers and fpsr + // + ld8 r16 = [r10], J_B1-J_FPSR // get fpsr + ld8 r17 = [r11], J_B2-J_B0 // get return pointer + ;; + mov ar.fpsr = r16 + mov b0 = r17 + ld8 r2 = [r10], J_B3-J_B1 + ld8 r3 = [r11], J_B4-J_B2 + ;; + mov b1 = r2 + mov b2 = r3 + ld8 r2 = [r10], J_B5-J_B3 + ld8 r3 = [r11] + ;; + mov b3 = r2 + mov b4 = r3 + ld8 r2 = [r10] + ld8 r21 = [r32] // get user unat + ;; + mov b5 = r2 + mov ar.unat = r21 + + // + // invalidate ALAT + // + invala ;; + + br.ret.sptk b0 + .endp longjmp diff --git a/usr/klibc/arch/ia64/sigaction.c b/usr/klibc/arch/ia64/sigaction.c new file mode 100644 index 0000000..3d2dbd4 --- /dev/null +++ b/usr/klibc/arch/ia64/sigaction.c @@ -0,0 +1,64 @@ +/* + * sigaction.c + */ + +#include <signal.h> +#include <sys/syscall.h> + +/* We use -mno-pic so our function pointers are straight to the function entry + point, but the kernel always expects a descriptor. Thus we create a fake + descriptor for each possible signal, update it, and pass that to the kernel + instead (the descriptor must remain valid after returning from sigaction + until it is replaced). */ +static struct { + uintptr_t entry; + uintptr_t gp; +} signal_descriptors[_NSIG]; + +__extern int ____rt_sigaction(int, const struct sigaction *, struct sigaction *, + size_t); + +int __rt_sigaction(int sig, struct sigaction *act, + struct sigaction *oact, size_t size) +{ + sigset_t signal_mask, old_signal_mask; + uintptr_t old_entry; + int rv; + + if (sig < 0 || sig >= _NSIG) { + errno = EINVAL; + return -1; + } + + /* Mask the signal to avoid races on access to its descriptor */ + sigemptyset(&signal_mask); + sigaddset(&signal_mask, sig); + rv = sigprocmask(SIG_BLOCK, &signal_mask, &old_signal_mask); + if (rv) + return -1; + + if (oact) { + old_entry = signal_descriptors[sig].entry; + } + + if (act && act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) { + signal_descriptors[sig].entry = (uintptr_t)act->sa_handler; + act->sa_handler = + (__sighandler_t)(uintptr_t)&signal_descriptors[sig]; + } + + rv = ____rt_sigaction(sig, act, oact, size); + + if (rv) + signal_descriptors[sig].entry = old_entry; + + /* Restore signal mask */ + (void)sigprocmask(SIG_SETMASK, &old_signal_mask, NULL); + + if (oact && oact->sa_handler != SIG_IGN && + oact->sa_handler != SIG_DFL) { + oact->sa_handler = (__sighandler_t)old_entry; + } + + return rv; +} diff --git a/usr/klibc/arch/ia64/syscall.S b/usr/klibc/arch/ia64/syscall.S new file mode 100644 index 0000000..155fdd8 --- /dev/null +++ b/usr/klibc/arch/ia64/syscall.S @@ -0,0 +1,18 @@ +# +# arch/ia64/syscall.S +# + +#include <asm/unistd.h> + + .text + .align 32 + .proc __syscall_error + .globl __syscall_error +__syscall_error: + movl r2 = errno + ;; + st4 [r2] = r8 + mov r8 = -1 + br.ret.sptk.many b0 + .size __syscall_error, .-__syscall_error + .endp __syscall_error diff --git a/usr/klibc/arch/ia64/sysstub.ph b/usr/klibc/arch/ia64/sysstub.ph new file mode 100644 index 0000000..8e686c6 --- /dev/null +++ b/usr/klibc/arch/ia64/sysstub.ph @@ -0,0 +1,29 @@ +# -*- perl -*- +# +# arch/ia64/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.text\n"; + print OUT "\t.align 32\n"; + print OUT "\t.proc ${fname}\n"; + print OUT "\t.globl ${fname}\n"; + print OUT "${fname}:\n"; + print OUT "\tmov\tr15 = __NR_${sname}\n"; + print OUT "\tbreak __BREAK_SYSCALL\n"; + print OUT "\tcmp.eq p6,p0 = -1,r10\n"; + print OUT "(p6)\tbr.few __syscall_error\n"; + print OUT "\tbr.ret.sptk.many b0\n"; + print OUT "\t.size\t${fname},.-${fname}\n"; + print OUT "\t.endp\t${fname}\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/ia64/vfork.S b/usr/klibc/arch/ia64/vfork.S new file mode 100644 index 0000000..e513188 --- /dev/null +++ b/usr/klibc/arch/ia64/vfork.S @@ -0,0 +1,37 @@ +/* + * ia64 specific vfork syscall + * + * Written By: Martin Hicks <mort@wildopensource.com> + * + */ + +/* This syscall is a special case of the clone syscall */ +#include <asm/unistd.h> +#include <asm/signal.h> + +/* These are redefined here because linux/sched.h isn't safe for + * inclusion in asm. + */ +#define CLONE_VM 0x00000100 /* set if VM shared between processes */ +#define CLONE_VFORK 0x00004000 /* set if parent wants the child to wake it up on exit */ + +/* pid_t vfork(void) */ +/* Implemented as clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0) */ + + .align 32 + .proc vfork + .global vfork +vfork: + alloc r2=ar.pfs,0,0,2,0 + mov r15=__NR_clone + mov out0=CLONE_VM|CLONE_VFORK|SIGCHLD + mov out1=0 + break 0x100000 // Do the syscall + cmp.eq p7,p6 = -1,r10 + ;; +(p7) movl r14 = errno + ;; +(p7) st4 [r14]=r8 +(p7) mov r8=-1 + br.ret.sptk.many b0 + .endp vfork diff --git a/usr/klibc/arch/loongarch64/Kbuild b/usr/klibc/arch/loongarch64/Kbuild new file mode 100644 index 0000000..60c7660 --- /dev/null +++ b/usr/klibc/arch/loongarch64/Kbuild @@ -0,0 +1,7 @@ +# +# klibc files for LoongArch64 +# + +klib-y := setjmp.o syscall.o +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/loongarch64/MCONFIG b/usr/klibc/arch/loongarch64/MCONFIG new file mode 100644 index 0000000..0ef5eae --- /dev/null +++ b/usr/klibc/arch/loongarch64/MCONFIG @@ -0,0 +1,23 @@ +# -*- makefile -*- +# +# arch/loongarch64/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCOPTFLAGS += -Os -fomit-frame-pointer +KLIBCBITSIZE = 64 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 4862 MB - normal binaries start at 4608 MB. Non-PIC jumps usually +# use the BL instruction which requires a destination between -128M +# to 128M. Since we can't put ourselves below the normal load +# address, use the very top of the 128M region (minus 2MB) +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x127E00000 + +# Kernel has never used stack trampolines +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/loongarch64/crt0.S b/usr/klibc/arch/loongarch64/crt0.S new file mode 100644 index 0000000..cb3da66 --- /dev/null +++ b/usr/klibc/arch/loongarch64/crt0.S @@ -0,0 +1,18 @@ +# +# arch/loongarch64/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + +#include <machine/asm.h> + +ENTRY(_start) + move $a0, $sp # Pointer to ELF entry structure + move $a1, $zero # No onexit pointer + bstrins.d $sp, $zero, 3, 0 # Align stack to 16 bytes + bl __libc_init +END(_start) diff --git a/usr/klibc/arch/loongarch64/setjmp.S b/usr/klibc/arch/loongarch64/setjmp.S new file mode 100644 index 0000000..1177988 --- /dev/null +++ b/usr/klibc/arch/loongarch64/setjmp.S @@ -0,0 +1,50 @@ +/* + * arch/loongarch64/setjmp.S + * + * setjmp/longjmp for the LoongArch64 architecture + * + * The jmp_buf is assumed to contain the following, in order: + * pc (ra) + * sp + * r21 + * fp + * s0..s8 + */ + +#include <machine/asm.h> + +ENTRY(setjmp) + st.d $ra, $a0, 0 + st.d $sp, $a0, 8 + st.d $r21, $a0, 16 + st.d $fp, $a0, 24 + st.d $s0, $a0, 32 + st.d $s1, $a0, 40 + st.d $s2, $a0, 48 + st.d $s3, $a0, 56 + st.d $s4, $a0, 64 + st.d $s5, $a0, 72 + st.d $s6, $a0, 80 + st.d $s7, $a0, 88 + st.d $s8, $a0, 96 + move $a0, $zero + jr $ra +END(setjmp) + +ENTRY(longjmp) + ld.d $ra, $a0, 0 + ld.d $sp, $a0, 8 + ld.d $r21, $a0, 16 + ld.d $fp, $a0, 24 + ld.d $s0, $a0, 32 + ld.d $s1, $a0, 40 + ld.d $s2, $a0, 48 + ld.d $s3, $a0, 56 + ld.d $s4, $a0, 64 + ld.d $s5, $a0, 72 + ld.d $s6, $a0, 80 + ld.d $s7, $a0, 88 + ld.d $s8, $a0, 96 + move $a0, $a1 + jr $ra +END(longjmp) diff --git a/usr/klibc/arch/loongarch64/syscall.S b/usr/klibc/arch/loongarch64/syscall.S new file mode 100644 index 0000000..9a2ed46 --- /dev/null +++ b/usr/klibc/arch/loongarch64/syscall.S @@ -0,0 +1,13 @@ +#include <machine/asm.h> +#include <asm/unistd.h> + +ENTRY(__syscall_common) + syscall 0 + li.w $t0, -4096 + bleu $a0, $t0, 1f + sub.d $a0, $zero, $a0 + la $t0, errno + st.d $a0, $t0, 0 + li.w $a0, -1 +1: jr $ra +END(__syscall_common) diff --git a/usr/klibc/arch/loongarch64/sysstub.ph b/usr/klibc/arch/loongarch64/sysstub.ph new file mode 100644 index 0000000..699bdc2 --- /dev/null +++ b/usr/klibc/arch/loongarch64/sysstub.ph @@ -0,0 +1,26 @@ +# -*- perl -*- +# +# arch/loongarch64/sysstub.ph +# +# Script to generate system call stubs +# + +# On LoongArch64, most system calls follow the standard convention, with +# the system call number in a7 and the return value in a0. + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + $stype = $stype || 'common'; + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <machine/asm.h>\n"; + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "ENTRY(${fname})\n"; + print OUT "\tli.w\t\$a7, __NR_${sname}\n"; + print OUT "\tb\t__syscall_${stype}\n"; + print OUT "END(${fname})\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/m68k/Kbuild b/usr/klibc/arch/m68k/Kbuild new file mode 100644 index 0000000..d56ae0e --- /dev/null +++ b/usr/klibc/arch/m68k/Kbuild @@ -0,0 +1,9 @@ +# +# klibc files for m68k +# + +klib-y := setjmp.o syscall.o vfork.o +klib-y += open.o openat.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/m68k/MCONFIG b/usr/klibc/arch/m68k/MCONFIG new file mode 100644 index 0000000..79bfbac --- /dev/null +++ b/usr/klibc/arch/m68k/MCONFIG @@ -0,0 +1,23 @@ +# -*- makefile -*- +# +# arch/m68k/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCOPTFLAGS += -Os -fomit-frame-pointer +KLIBCBITSIZE = 32 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 2816 MB - normal binaries start at 2048 MB if I read the link +# script right. Not sure if there is a fundamental reason +# to not duck below the halfway point... +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0xb0000000 + +# Kernel uses stack trampoline for signal return. Also m68k +# page tables do not have an execute permission bit. +KLIBCEXECSTACK := y diff --git a/usr/klibc/arch/m68k/crt0.S b/usr/klibc/arch/m68k/crt0.S new file mode 100644 index 0000000..fbf6f13 --- /dev/null +++ b/usr/klibc/arch/m68k/crt0.S @@ -0,0 +1,27 @@ +# +# arch/m68k/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + + .text + .align 4 + .type _start,@function + .globl _start +_start: + # Zero out the frame pointer to be nice to the debugger + movea.l #0,%a6 + # Save the address of the ELF argument array + move.l %a7, %d0 + # Push a zero on the stack in lieu of atexit pointer + clr.l -(%sp) + # Push ELF argument pointer on the stack + move.l %d0, -(%a7) + + jbsr __libc_init + + .size _start, .-_start diff --git a/usr/klibc/arch/m68k/open.S b/usr/klibc/arch/m68k/open.S new file mode 100644 index 0000000..c9a7ee3 --- /dev/null +++ b/usr/klibc/arch/m68k/open.S @@ -0,0 +1,22 @@ +/* + * arch/m68k/open.S + * + * Handle the open() system call - oddball due to the varadic + * prototype, which forces the use of the cdecl calling convention, + * and the need for O_LARGEFILE. + */ + +#include <asm/unistd.h> + +/* <asm/fcntl.h>, despite the name, isn't assembly-safe */ +#define O_LARGEFILE 0400000 + + .globl open + .type open,@function + +open: + or.l # O_LARGEFILE, 8(%sp) + move.l # __NR_open, %d0 + br __syscall_common + + .size open,.-open diff --git a/usr/klibc/arch/m68k/openat.S b/usr/klibc/arch/m68k/openat.S new file mode 100644 index 0000000..25d8a1a --- /dev/null +++ b/usr/klibc/arch/m68k/openat.S @@ -0,0 +1,26 @@ +/* + * arch/m68k/openat.S + * + * Handle the openat() system call - oddball due to the varadic + * prototype, which forces the use of the cdecl calling convention, + * and the need for O_LARGEFILE. + */ + +#include <asm/unistd.h> + +/* <asm/fcntl.h>, despite the name, isn't assembly-safe */ +#define O_LARGEFILE 0400000 + +#ifdef __NR_openat /* Don't build if kernel headers too old */ + + .globl openat + .type openat,@function + +openat: + or.l # O_LARGEFILE, 12(%sp) + move.l # __NR_openat, %d0 + br __syscall_common + + .size openat,.-openat + +#endif diff --git a/usr/klibc/arch/m68k/setjmp.S b/usr/klibc/arch/m68k/setjmp.S new file mode 100644 index 0000000..1b3591e --- /dev/null +++ b/usr/klibc/arch/m68k/setjmp.S @@ -0,0 +1,43 @@ +# +# arch/m68k/setjmp.S +# +# setjmp/longjmp for the m68k architecture +# + +# +# The jmp_buf is assumed to contain the following, in order: +# %d2..%d7 +# %a2..%a7 +# return address +# + + .text + .align 2 + .globl setjmp + .type setjmp, @function +setjmp: + move.l (%sp)+, %d0 | Return address + movea.l (%sp), %a0 | Buffer address + | Postincrement mode is not permitted here... + movem.l %d2-%d7/%a2-%a7, (%a0) + move.l %d0, 48(%a0) | Return address + move.l %d0, -(%sp) | Restore return address + clr.l %d0 | Return value + movea.l %d0, %a0 | Redundant return... + rts + + .size setjmp,.-setjmp + + .text + .align 2 + .globl longjmp + .type longjmp, @function +longjmp: + move.l 4(%sp), %a0 | Buffer address + move.l 8(%sp), %d0 | Return value + movem.l (%a0)+, %d2-%d7/%a2-%a7 + movea.l (%a0), %a1 + movea.l %d0, %a0 | Redundant return... + jmp.l (%a1) + + .size longjmp,.-longjmp diff --git a/usr/klibc/arch/m68k/syscall.S b/usr/klibc/arch/m68k/syscall.S new file mode 100644 index 0000000..c909e2a --- /dev/null +++ b/usr/klibc/arch/m68k/syscall.S @@ -0,0 +1,53 @@ +/* + * arch/m68k/syscall.S + * + * Common tail-handling code for system calls. + * + * The arguments are on the stack; the system call number in %d0. + */ + + .text + .align 2 + .globl __syscall_common + .type __syscall_common, @function +__syscall_common: + /* + * According to eglibc, separate moves are faster than movem; + * speed is important and this code is not duplicated anyway, + * so we do the same here. We use %a1 as scratch register for + * saving; syscall arguments are to be in %d1 to %d5 and %a0. + */ + move.l 24(%sp), %a0 /* orig.sp+24: arg 6 */ + move.l %d5, -(%sp) /* push d5 (callee saved) */ + move.l 24(%sp), %d5 /* orig.sp+20: arg 5 */ + move.l %d4, -(%sp) /* push d4 (callee saved) */ + move.l 24(%sp), %d4 /* orig.sp+16: arg 4 */ + move.l %d3, -(%sp) /* push d3 (callee saved) */ + move.l 24(%sp), %d3 /* orig.sp+12: arg 3 */ + move.l %d2, %a1 /* save d2 (callee saved) in a1 */ + move.l 20(%sp), %d2 /* orig.sp+8: arg 2 */ + move.l 16(%sp), %d1 /* orig.sp+4: arg 1 */ + trap #0 + move.l %a1, %d2 /* restore d2 from a1 (scratch) */ + move.l (%sp)+, %d3 /* pop d3..d5, see above */ + move.l (%sp)+, %d4 + move.l (%sp)+, %d5 + + /* syscall is done, result in %d0, registers are restored */ + .globl __syscall_checkandout +__syscall_checkandout: + /* now check for error */ + cmp.l #-4095, %d0 + bcs.l 1f /* jump if _not_ error */ + + /* prepare for error return */ + neg.l %d0 + move.l %d0, (errno) + move.l #-1, %d0 + /* fallthrough into common return path */ + +1: /* copy return value to %a0 for syscalls returning pointers */ + move.l %d0, %a0 + rts + + .size __syscall_common,.-__syscall_common diff --git a/usr/klibc/arch/m68k/sysstub.ph b/usr/klibc/arch/m68k/sysstub.ph new file mode 100644 index 0000000..78c239d --- /dev/null +++ b/usr/klibc/arch/m68k/sysstub.ph @@ -0,0 +1,26 @@ +# -*- perl -*- +# +# arch/m68k/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.type ${fname},\@function\n"; + print OUT "\t.globl ${fname}\n"; + print OUT "${fname}:\n"; + + $stype = 'common' if ( $stype eq '' ); + + print OUT "\tmove.l\t# __NR_${sname}, %d0\n"; + print OUT "\tbr\t__syscall_$stype\n"; + print OUT "\t.size ${fname},.-${fname}\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/m68k/vfork.S b/usr/klibc/arch/m68k/vfork.S new file mode 100644 index 0000000..98170a6 --- /dev/null +++ b/usr/klibc/arch/m68k/vfork.S @@ -0,0 +1,23 @@ +# +# usr/klibc/arch/m68k/vfork.S +# +# vfork is nasty - there must be nothing at all on the stack above +# the stack frame of the enclosing function. +# + +#include <asm/unistd.h> + + .text + .align 2 + .globl vfork + .type vfork, @function +vfork: + move.l (%sp)+, %d1 /* Return address */ + move.l # __NR_vfork, %d0 + trap #0 + move.l %d1, -(%sp) /* restore stack */ + + /* fallthrough into common code from syscall.S */ + bra __syscall_checkandout + + .size vfork, .-vfork diff --git a/usr/klibc/arch/mips/Kbuild b/usr/klibc/arch/mips/Kbuild new file mode 100644 index 0000000..972e450 --- /dev/null +++ b/usr/klibc/arch/mips/Kbuild @@ -0,0 +1,14 @@ +# +# klibc files for mips +# + +klib-y := pipe.o vfork.o setjmp.o syscall.o + +klib-y += ../../libgcc/__clzsi2.o ../../libgcc/__ashldi3.o +klib-y += ../../libgcc/__ashrdi3.o ../../libgcc/__lshrdi3.o +klib-y += ../../libgcc/__divdi3.o ../../libgcc/__moddi3.o +klib-y += ../../libgcc/__udivdi3.o ../../libgcc/__umoddi3.o +klib-y += ../../libgcc/__udivmoddi4.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/mips/MCONFIG b/usr/klibc/arch/mips/MCONFIG new file mode 100644 index 0000000..48cda8e --- /dev/null +++ b/usr/klibc/arch/mips/MCONFIG @@ -0,0 +1,21 @@ +# -*- makefile -*- +# +# arch/mips/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCARCHREQFLAGS = -fno-pic -mno-abicalls -G 0 +KLIBCOPTFLAGS += -Os +KLIBCBITSIZE = 32 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 2 MB - normal binaries start at 4 MB +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x00200000 + +# Kernel uses vDSO for signal return since 2.6.34 +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/mips/crt0.S b/usr/klibc/arch/mips/crt0.S new file mode 100644 index 0000000..47d7d8f --- /dev/null +++ b/usr/klibc/arch/mips/crt0.S @@ -0,0 +1,22 @@ +# +# arch/mips/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + +#include <machine/asm.h> + +NESTED(__start, 0, ra) + move a0, sp # Pointer to ELF entry structure + move a1, v0 # Kernel-provided atexit() pointer + and sp, -8 # Align stack to 8 bytes + subu sp, 16 # Allocate 16 bytes for function call + + jal __libc_init + teq zero, zero # Crash if we return + + END(__start) diff --git a/usr/klibc/arch/mips/pipe.S b/usr/klibc/arch/mips/pipe.S new file mode 100644 index 0000000..932fc08 --- /dev/null +++ b/usr/klibc/arch/mips/pipe.S @@ -0,0 +1,15 @@ +#include <machine/asm.h> +#include <asm/unistd.h> + +LEAF(pipe) + li v0, __NR_pipe + syscall + bnez a3, 1f + sw v0, (a0) + sw v1, 4(a0) + li v0, 0 + b 2f +1: sw v0, errno + li v0, -1 +2: jr ra + END(pipe) diff --git a/usr/klibc/arch/mips/setjmp.S b/usr/klibc/arch/mips/setjmp.S new file mode 100644 index 0000000..9145dbc --- /dev/null +++ b/usr/klibc/arch/mips/setjmp.S @@ -0,0 +1,50 @@ +# +# arch/mips/setjmp.S +# +# setjmp/longjmp for the MIPS architecture +# +# The jmp_buf is assumed to contain the following, in order: +# s0..s7 +# gp +# sp +# s8 +# ra +# + +#include <machine/asm.h> + +LEAF(setjmp) + sw s0, 0(a0) + sw s1, 4(a0) + sw s2, 8(a0) + sw s3, 12(a0) + sw s4, 16(a0) + sw s5, 20(a0) + sw s6, 24(a0) + sw s7, 28(a0) + sw gp, 32(a0) + sw sp, 36(a0) + sw s8, 40(a0) + sw ra, 44(a0) + move v0,zero + jr ra + + END(setjmp) + +LEAF(longjmp) + lw s0, 0(a0) + lw s1, 4(a0) + lw s2, 8(a0) + lw s3, 12(a0) + lw s4, 16(a0) + lw s5, 20(a0) + lw s6, 24(a0) + lw s7, 28(a0) + lw gp, 32(a0) + lw sp, 36(a0) + lw s8, 40(a0) + lw ra, 44(a0) + move v0,a1 + jr ra + + END(longjmp) diff --git a/usr/klibc/arch/mips/syscall.S b/usr/klibc/arch/mips/syscall.S new file mode 100644 index 0000000..a5cc73d --- /dev/null +++ b/usr/klibc/arch/mips/syscall.S @@ -0,0 +1,11 @@ +#include <machine/asm.h> +#include <asm/unistd.h> + + +LEAF(__syscall_common) + syscall + beqz a3, 1f + sw v0, errno + li v0, -1 +1: jr ra + END(__syscall_common) diff --git a/usr/klibc/arch/mips/sysstub.ph b/usr/klibc/arch/mips/sysstub.ph new file mode 100644 index 0000000..a3475b7 --- /dev/null +++ b/usr/klibc/arch/mips/sysstub.ph @@ -0,0 +1,27 @@ +# -*- perl -*- +# +# arch/mips/sysstub.ph +# +# Script to generate system call stubs +# + +# On MIPS, most system calls follow the standard convention, with the +# system call number in r0 (v0), return an error value in r19 (a3) as +# well as the return value in r0 (v0). + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + $stype = $stype || 'common'; + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <machine/asm.h>\n"; + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "LEAF(${fname})\n"; + print OUT "\t li\tv0, __NR_${sname}\n"; + print OUT "\tj\t__syscall_${stype}\n"; + print OUT "\tEND(${fname})\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/mips/vfork.S b/usr/klibc/arch/mips/vfork.S new file mode 100644 index 0000000..ae36d5f --- /dev/null +++ b/usr/klibc/arch/mips/vfork.S @@ -0,0 +1,14 @@ +#include <machine/asm.h> +#include <asm/unistd.h> + +#define CLONE_VM 0x00000100 +#define CLONE_VFORK 0x00004000 +#define SIGCHLD 18 + + +LEAF(vfork) + li a0, CLONE_VFORK | CLONE_VM | SIGCHLD + li a1, 0 + li v0, __NR_clone + j __syscall_common + END(vfork) diff --git a/usr/klibc/arch/mips64/Kbuild b/usr/klibc/arch/mips64/Kbuild new file mode 100644 index 0000000..6fe3b53 --- /dev/null +++ b/usr/klibc/arch/mips64/Kbuild @@ -0,0 +1,14 @@ +# +# klibc files for mips64 +# + +klib-y := ../mips/pipe.o ../mips/vfork.o setjmp.o ../mips/syscall.o + +klib-y += ../../libgcc/__clzsi2.o ../../libgcc/__ashldi3.o +klib-y += ../../libgcc/__ashrdi3.o ../../libgcc/__lshrdi3.o +klib-y += ../../libgcc/__divdi3.o ../../libgcc/__moddi3.o +klib-y += ../../libgcc/__udivdi3.o ../../libgcc/__umoddi3.o +klib-y += ../../libgcc/__udivmoddi4.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/mips64/MCONFIG b/usr/klibc/arch/mips64/MCONFIG new file mode 100644 index 0000000..e078dad --- /dev/null +++ b/usr/klibc/arch/mips64/MCONFIG @@ -0,0 +1,34 @@ +# -*- makefile -*- +# +# arch/mips64/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCARCHREQFLAGS = -fno-pic -mno-abicalls -G 0 +KLIBCOPTFLAGS += -Os +KLIBCBITSIZE = 64 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture. +# Normal binaries start at 0x120000000 (4608 MiB). +# * On R5 and earlier, non-PIC jumps usually use the JAL instruction +# which requires a destination within the same 256 MiB aligned +# region. Since we can't put ourselves below the normal load +# address, use the very top of the 256 MiB region (minus 2 MiB). +# * On R6, non-PIC jumps may use either the JAL instruction or the +# BALC instruction which requires a destination within ±128 MiB. Put +# ourselves just under 128 MiB above the executable base address. +ifneq ($(CONFIG_KLIBC_MIPS_USE_CB),y) +KLIBCARCHREQFLAGS += $(call cc-option,-mcompact-branches=never) +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x12FE00000 +else +KLIBCARCHREQFLAGS += -mcompact-branches=optimal +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x127E00000 +endif + +# Kernel uses vDSO for signal return since 2.6.34 +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/mips64/crt0.S b/usr/klibc/arch/mips64/crt0.S new file mode 100644 index 0000000..3f1c2a9 --- /dev/null +++ b/usr/klibc/arch/mips64/crt0.S @@ -0,0 +1,21 @@ +# +# arch/mips64/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + +#include <machine/asm.h> + +NESTED(__start, 0, ra) + move a0, sp # Pointer to ELF entry structure + move a1, v0 # Kernel-provided atexit() pointer + and sp, -16 # Align stack to 16 bytes + + jal __libc_init + teq zero, zero # Crash if we return + + END(__start) diff --git a/usr/klibc/arch/mips64/setjmp.S b/usr/klibc/arch/mips64/setjmp.S new file mode 100644 index 0000000..5d902e2 --- /dev/null +++ b/usr/klibc/arch/mips64/setjmp.S @@ -0,0 +1,50 @@ +# +# arch/mips64/setjmp.S +# +# setjmp/longjmp for the MIPS architecture +# +# The jmp_buf is assumed to contain the following, in order: +# s0..s7 +# gp +# sp +# s8 +# ra +# + +#include <machine/asm.h> + +LEAF(setjmp) + sd s0, 0(a0) + sd s1, 8(a0) + sd s2, 16(a0) + sd s3, 24(a0) + sd s4, 32(a0) + sd s5, 40(a0) + sd s6, 48(a0) + sd s7, 56(a0) + sd gp, 64(a0) + sd sp, 72(a0) + sd s8, 80(a0) + sd ra, 88(a0) + move v0, zero + jr ra + + END(setjmp) + +LEAF(longjmp) + ld s0, 0(a0) + ld s1, 8(a0) + ld s2, 16(a0) + ld s3, 24(a0) + ld s4, 32(a0) + ld s5, 40(a0) + ld s6, 48(a0) + ld s7, 56(a0) + ld gp, 64(a0) + ld sp, 72(a0) + ld s8, 80(a0) + ld ra, 88(a0) + move v0, a1 + jr ra + + END(longjmp) diff --git a/usr/klibc/arch/mips64/sysstub.ph b/usr/klibc/arch/mips64/sysstub.ph new file mode 100644 index 0000000..57af657 --- /dev/null +++ b/usr/klibc/arch/mips64/sysstub.ph @@ -0,0 +1,27 @@ +# -*- perl -*- +# +# arch/mips64/sysstub.ph +# +# Script to generate system call stubs +# + +# On MIPS, most system calls follow the standard convention, with the +# system call number in r0 (v0), return an error value in r19 (a3) as +# well as the return value in r0 (v0). + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + $stype = $stype || 'common'; + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <machine/asm.h>\n"; + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "LEAF(${fname})\n"; + print OUT "\t li\tv0, __NR_${sname}\n"; + print OUT "\tj\t__syscall_${stype}\n"; + print OUT "\tEND(${fname})\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/parisc/Kbuild b/usr/klibc/arch/parisc/Kbuild new file mode 100644 index 0000000..57ca5c2 --- /dev/null +++ b/usr/klibc/arch/parisc/Kbuild @@ -0,0 +1,8 @@ +# +# klibc files for parisc +# + +klib-y := setjmp.o syscall.o vfork.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/parisc/MCONFIG b/usr/klibc/arch/parisc/MCONFIG new file mode 100644 index 0000000..bc8bd9b --- /dev/null +++ b/usr/klibc/arch/parisc/MCONFIG @@ -0,0 +1,15 @@ +# -*- makefile -*- +# +# arch/parisc/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCOPTFLAGS += -Os -fomit-frame-pointer +KLIBCBITSIZE = 32 +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x40001000 + +# Kernel uses stack trampoline for signal return +KLIBCEXECSTACK := y diff --git a/usr/klibc/arch/parisc/crt0.S b/usr/klibc/arch/parisc/crt0.S new file mode 100644 index 0000000..0922446 --- /dev/null +++ b/usr/klibc/arch/parisc/crt0.S @@ -0,0 +1,37 @@ + .align 4 + + .import $global$, data + .import __libc_init, code + + .global _start + .export _start, ENTRY + .type _start,@function + + .proc + .callinfo + +_start: +/* extend the stack by 64-bytes */ + ldo 64(%sp), %sp + +/* %r25 = argc + * %r24 = argv + * envp = argv + (argc + 1) + * elfdata = (argv - 4) + */ + ldo -4(%r24), %r26 + +/* load global data */ + ldil L%$global$, %dp + ldo R%$global$(%dp), %dp + +/* parisc abi puts the atexit pointer in %r23, see ELF_PLAT_INIT() */ + copy %r23, %r25 + +/* branch to __libc_init */ + bl __libc_init,%r2 + nop +/* break miserably if we ever return */ + iitlbp %r0,(%sr0,%r0) /* illegal instruction */ + nop + .procend diff --git a/usr/klibc/arch/parisc/setjmp.S b/usr/klibc/arch/parisc/setjmp.S new file mode 100644 index 0000000..c8d766c --- /dev/null +++ b/usr/klibc/arch/parisc/setjmp.S @@ -0,0 +1,88 @@ +/* + * parisc specific setjmp/longjmp routines + * + */ + + .text + .align 4 + .global setjmp + .export setjmp, code + .proc + .callinfo +setjmp: + stw %r3,0(%r26) + stw %r4,8(%r26) + stw %r5,12(%r26) + stw %r6,16(%r26) + stw %r7,20(%r26) + stw %r8,24(%r26) + stw %r9,28(%r26) + stw %r10,32(%r26) + stw %r11,36(%r26) + stw %r12,40(%r26) + stw %r13,44(%r26) + stw %r14,48(%r26) + stw %r15,52(%r26) + stw %r16,56(%r26) + stw %r17,60(%r26) + stw %r18,64(%r26) + stw %r19,68(%r26) + stw %r27,72(%r26) + stw %r30,76(%r26) + stw %rp,80(%r26) + ldo 88(%r26),%r19 + fstd,ma %fr12,8(%r19) + fstd,ma %fr13,8(%r19) + fstd,ma %fr14,8(%r19) + fstd,ma %fr15,8(%r19) + fstd,ma %fr16,8(%r19) + fstd,ma %fr17,8(%r19) + fstd,ma %fr18,8(%r19) + fstd,ma %fr19,8(%r19) + fstd,ma %fr20,8(%r19) + fstd %fr21,0(%r19) + bv %r0(%rp) + copy %r0,%r28 + .procend + + .text + .align 4 + .global longjmp + .export longjmp, code + .proc + .callinfo +longjmp: + ldw 0(%r26),%r3 + ldw 8(%r26),%r4 + ldw 12(%r26),%r5 + ldw 16(%r26),%r6 + ldw 20(%r26),%r7 + ldw 24(%r26),%r8 + ldw 28(%r26),%r9 + ldw 32(%r26),%r10 + ldw 36(%r26),%r11 + ldw 40(%r26),%r12 + ldw 44(%r26),%r13 + ldw 48(%r26),%r14 + ldw 52(%r26),%r15 + ldw 56(%r26),%r16 + ldw 60(%r26),%r17 + ldw 64(%r26),%r18 + ldw 68(%r26),%r19 + ldw 72(%r26),%r27 + ldw 76(%r26),%r30 + ldw 80(%r26),%rp + ldo 88(%r26),%r20 + fldd,ma 8(%r20),%fr12 + fldd,ma 8(%r20),%fr13 + fldd,ma 8(%r20),%fr14 + fldd,ma 8(%r20),%fr15 + fldd,ma 8(%r20),%fr16 + fldd,ma 8(%r20),%fr17 + fldd,ma 8(%r20),%fr18 + fldd,ma 8(%r20),%fr19 + fldd,ma 8(%r20),%fr20 + fldd 0(%r20),%fr21 + bv %r0(%rp) + copy %r25,%r28 + .procend diff --git a/usr/klibc/arch/parisc/syscall.S b/usr/klibc/arch/parisc/syscall.S new file mode 100644 index 0000000..0ff2a65 --- /dev/null +++ b/usr/klibc/arch/parisc/syscall.S @@ -0,0 +1,36 @@ +/* + * arch/parisc/syscall.S + * + * %r20 contains the system call number, %r2 contains whence we came + * + */ + + .text + .align 64 ; cache-width aligned + .globl __syscall_common + .type __syscall_common,@function +__syscall_common: + ldo 0x40(%sp),%sp + stw %rp,-0x54(%sp) ; save return pointer + + ldw -0x74(%sp),%r22 ; %arg4 + ldw -0x78(%sp),%r21 ; %arg5 + + ble 0x100(%sr2, %r0) ; jump to gateway page + nop ; can we move a load here? + + ldi -0x1000,%r19 ; %r19 = -4096 + sub %r0,%ret0,%r22 ; %r22 = -%ret0 + cmpb,>>=,n %r19,%ret0,1f ; if %ret0 >= -4096UL + ldi -1,%ret0 ; nullified on taken forward + + /* store %r22 to errno... */ + ldil L%errno,%r1 + ldo R%errno(%r1),%r1 + stw %r22,0(%r1) +1: + ldw -0x54(%sp),%rp ; restore return pointer + bv %r0(%rp) ; jump back + ldo -0x40(%sp),%sp + + .size __syscall_common,.-__syscall_common diff --git a/usr/klibc/arch/parisc/sysstub.ph b/usr/klibc/arch/parisc/sysstub.ph new file mode 100644 index 0000000..e2196ac --- /dev/null +++ b/usr/klibc/arch/parisc/sysstub.ph @@ -0,0 +1,28 @@ +# -*- perl -*- +# +# arch/parisc/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.text\n"; + print OUT "\t.align 4\n"; + print OUT "\t.import __syscall_common, code\n"; + print OUT "\t.global ${fname}\n"; + print OUT "\t.export ${fname}, code\n"; + print OUT "\t.proc\n"; + print OUT "\.callinfo\n"; + print OUT "${fname}:\n"; + print OUT "\tb\t__syscall_common\n"; + print OUT "\t ldo\t__NR_${sname}(%r0),%r20\n"; + print OUT "\t.procend\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/parisc/vfork.S b/usr/klibc/arch/parisc/vfork.S new file mode 100644 index 0000000..3868a8d --- /dev/null +++ b/usr/klibc/arch/parisc/vfork.S @@ -0,0 +1,32 @@ +/* + * arch/parisc/vfork.S, "vfork() me harder. ugh." -- kyle + * + * %rp contains whence we came, + * %rp is saved and restored across the syscall, thankfully. + * + */ + + .text + .align 64 ; cache-width aligned + .globl vfork + .type vfork,@function +vfork: + /* pid_t vfork(void) */ + ldi 113,%r20 + ble 0x100(%sr2, %r0) ; jump to gateway page + nop + + ldi -0x1000,%r19 ; %r19 = -4096 + sub %r0,%ret0,%r22 ; %r22 = -%ret0 + cmpb,>>=,n %r19,%ret0,1f ; if %ret0 >= -4096UL + ldi -1,%ret0 ; nullified on taken forward + + /* store %r22 to errno... */ + ldil L%errno,%r1 + ldo R%errno(%r1),%r1 + stw %r22,0(%r1) +1: + bv %r0(%rp) ; jump back + nop + + .size vfork,.-vfork diff --git a/usr/klibc/arch/ppc/Kbuild b/usr/klibc/arch/ppc/Kbuild new file mode 100644 index 0000000..699380f --- /dev/null +++ b/usr/klibc/arch/ppc/Kbuild @@ -0,0 +1,12 @@ +# +# klibc files for ppc +# + +klib-y := setjmp.o syscall.o + +klib-y += ../../libgcc/__divdi3.o ../../libgcc/__moddi3.o +klib-y += ../../libgcc/__udivdi3.o ../../libgcc/__umoddi3.o +klib-y += ../../libgcc/__udivmoddi4.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/ppc/MCONFIG b/usr/klibc/arch/ppc/MCONFIG new file mode 100644 index 0000000..db4806a --- /dev/null +++ b/usr/klibc/arch/ppc/MCONFIG @@ -0,0 +1,29 @@ +# -*- makefile -*- +# +# arch/ppc/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +gcc_m32_option := $(call cc-option, -m32, ) + +KLIBCOPTFLAGS += -Os +KLIBCLDFLAGS = -m elf32ppclinux +KLIBCARCHREQFLAGS += $(gcc_m32_option) + +KLIBCBITSIZE = 32 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 256-16 MB - normal binaries start at 256 MB, and jumps are limited +# to +/- 16 MB +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x0f800000 + +# The asm include files live in asm-powerpc +KLIBCASMARCH = powerpc + +# Kernel uses vDSO for signal return since 2.6.12 (compat) or 2.6.15 (native) +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/ppc/crt0.S b/usr/klibc/arch/ppc/crt0.S new file mode 100644 index 0000000..85b6dca --- /dev/null +++ b/usr/klibc/arch/ppc/crt0.S @@ -0,0 +1,23 @@ +# +# arch/ppc/crt0.S +# + + .text + .align 4 + .type _start,@function + .globl _start +_start: + stwu 1,-16(1) + addi 3,1,16 + /* + * the SVR4abippc.pdf specifies r7 as a pointer to + * a termination function point + * However, Section 8.4.1 of the LSB API docs say that + * The value to be placed into register r7, the termination + * function pointer, is not passed to the process. + * So we stub it out, instead. + */ + li 4,0 + bl __libc_init + + .size _start,.-_start diff --git a/usr/klibc/arch/ppc/setjmp.S b/usr/klibc/arch/ppc/setjmp.S new file mode 100644 index 0000000..e02b7da --- /dev/null +++ b/usr/klibc/arch/ppc/setjmp.S @@ -0,0 +1,34 @@ +# +# arch/ppc/setjmp.S +# +# Basic setjmp/longjmp implementation +# This file was derived from the equivalent file in NetBSD +# + + .text + .align 4 + .type setjmp,@function + .globl setjmp +setjmp: + mflr %r11 /* save return address */ + mfcr %r12 /* save condition register */ + mr %r10,%r1 /* save stack pointer */ + mr %r9,%r2 /* save GPR2 (not needed) */ + stmw %r9,0(%r3) /* save r9..r31 */ + li %r3,0 /* indicate success */ + blr /* return */ + + .size setjmp,.-setjmp + + .type longjmp,@function + .globl longjmp +longjmp: + lmw %r9,0(%r3) /* save r9..r31 */ + mtlr %r11 /* restore LR */ + mtcr %r12 /* restore CR */ + mr %r2,%r9 /* restore GPR2 (not needed) */ + mr %r1,%r10 /* restore stack */ + mr %r3,%r4 /* get return value */ + blr /* return */ + + .size longjmp,.-longjmp diff --git a/usr/klibc/arch/ppc/syscall.S b/usr/klibc/arch/ppc/syscall.S new file mode 100644 index 0000000..0a7c37c --- /dev/null +++ b/usr/klibc/arch/ppc/syscall.S @@ -0,0 +1,16 @@ +/* + * arch/ppc/syscall.S + * + * Common error-handling path for system calls. + */ + + .text + .align 2 + .globl __syscall_error + .type __syscall_error,@function +__syscall_error: + lis 9,errno@ha + stw 3,errno@l(9) + li 3,-1 + blr + .size __syscall_error,.-__syscall_error diff --git a/usr/klibc/arch/ppc/sysstub.ph b/usr/klibc/arch/ppc/sysstub.ph new file mode 100644 index 0000000..3b3916c --- /dev/null +++ b/usr/klibc/arch/ppc/sysstub.ph @@ -0,0 +1,25 @@ +# -*- perl -*- +# +# arch/ppc/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.type ${fname},\@function\n"; + print OUT "\t.globl ${fname}\n"; + print OUT "${fname}:\n"; + print OUT "\tli 0,__NR_${sname}\n"; + print OUT "\tsc\n"; + print OUT "\tbnslr\n"; + print OUT "\tb __syscall_error\n"; + print OUT "\t.size ${fname},.-${fname}\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/ppc64/Kbuild b/usr/klibc/arch/ppc64/Kbuild new file mode 100644 index 0000000..a39e91f --- /dev/null +++ b/usr/klibc/arch/ppc64/Kbuild @@ -0,0 +1,8 @@ +# +# klibc files for ppc64 +# + +klib-y := setjmp.o syscall.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/ppc64/MCONFIG b/usr/klibc/arch/ppc64/MCONFIG new file mode 100644 index 0000000..4326560 --- /dev/null +++ b/usr/klibc/arch/ppc64/MCONFIG @@ -0,0 +1,27 @@ +# -*- makefile -*- +# +# arch/ppc64/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCARCHREQFLAGS = -m64 +KLIBCARCHREQFLAGS += $(call cc-option, -mcall-aixdesc, ) +KLIBCARCHREQFLAGS += $(call cc-option, -mcmodel=small, ) +KLIBCOPTFLAGS += -Os +KLIBCBITSIZE = 64 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 256-16 MB - normal binaries start at 256 MB, and jumps are limited +# to +/- 16 MB +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x0f000000 + +# The asm include files live in asm-powerpc +KLIBCASMARCH = powerpc + +# Kernel uses vDSO for signal return since 2.6.12 +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/ppc64/crt0.S b/usr/klibc/arch/ppc64/crt0.S new file mode 100644 index 0000000..ed14534 --- /dev/null +++ b/usr/klibc/arch/ppc64/crt0.S @@ -0,0 +1,43 @@ +# +# arch/ppc64/crt0.S +# +# void _start(void) +# { +# /* Divine up argc, argv, and envp */ +# environ = envp; +# exit(main(argc, argv, envp)); +# } +# + + .text + .balign 4 + .globl _start +#if _CALL_ELF == 2 + .type _start,@function +_start: +#else + .section ".opd","aw" + .balign 8 +_start: + .quad ._start, .TOC.@tocbase, 0 + .previous + .type ._start,@function +._start: +#endif + +#if _CALL_ELF == 2 +0: addis 2,12,.TOC.-0b@ha + addi 2,2,.TOC.-0b@l +#endif + + stdu %r1,-32(%r1) + addi %r3,%r1,32 + li %r4,0 /* fini (unused) */ + b __libc_init + nop + +#if _CALL_ELF == 2 + .size _start,.-_start +#else + .size _start,.-._start +#endif diff --git a/usr/klibc/arch/ppc64/setjmp.S b/usr/klibc/arch/ppc64/setjmp.S new file mode 100644 index 0000000..ecf9717 --- /dev/null +++ b/usr/klibc/arch/ppc64/setjmp.S @@ -0,0 +1,103 @@ +# +# arch/ppc64/setjmp.S +# +# Basic setjmp/longjmp implementation +# + + .text + .balign 4 + .globl setjmp +#if _CALL_ELF == 2 + .type setjmp,@function +setjmp: +#else + .section ".opd","aw" + .balign 8 +setjmp: + .quad .setjmp, .TOC.@tocbase, 0 + .previous + .type .setjmp,@function + .globl .setjmp +.setjmp: +#endif + mflr %r11 /* save return address */ + mfcr %r12 /* save condition register */ + std %r2,0(%r3) /* save TOC pointer (not needed) */ + stdu %r1,8(%r3) /* save stack pointer */ + stdu %r11,8(%r3) + stdu %r12,8(%r3) + stdu %r13,8(%r3) /* save caller saved regs */ + stdu %r14,8(%r3) + stdu %r15,8(%r3) + stdu %r16,8(%r3) + stdu %r17,8(%r3) + stdu %r18,8(%r3) + stdu %r19,8(%r3) + stdu %r20,8(%r3) + stdu %r21,8(%r3) + stdu %r22,8(%r3) + stdu %r23,8(%r3) + stdu %r24,8(%r3) + stdu %r25,8(%r3) + stdu %r26,8(%r3) + stdu %r27,8(%r3) + stdu %r28,8(%r3) + stdu %r29,8(%r3) + stdu %r30,8(%r3) + std %r31,8(%r3) + li %r3,0 /* indicate success */ + blr /* return */ +#if _CALL_ELF == 2 + .size setjmp,.-setjmp +#else + .size setjmp,.-.setjmp +#endif + + .text + .balign 4 + .globl longjmp +#if _CALL_ELF == 2 + .type longjmp,@function +longjmp: +#else + .section ".opd","aw" + .balign 8 +longjmp: + .quad .longjmp, .TOC.@tocbase, 0 + .previous + .type .longjmp,@function + .globl .longjmp +.longjmp: +#endif + ld %r2,0(%r3) /* restore TOC pointer (not needed) */ + ldu %r1,8(%r3) /* restore stack */ + ldu %r11,8(%r3) + ldu %r12,8(%r3) + ldu %r13,8(%r3) /* restore caller saved regs */ + ldu %r14,8(%r3) + ldu %r15,8(%r3) + ldu %r16,8(%r3) + ldu %r17,8(%r3) + ldu %r18,8(%r3) + ldu %r19,8(%r3) + ldu %r20,8(%r3) + ldu %r21,8(%r3) + ldu %r22,8(%r3) + ldu %r23,8(%r3) + ldu %r24,8(%r3) + ldu %r25,8(%r3) + ldu %r26,8(%r3) + ldu %r27,8(%r3) + ldu %r28,8(%r3) + ldu %r29,8(%r3) + ldu %r30,8(%r3) + ld %r31,8(%r3) + mtlr %r11 /* restore LR */ + mtcr %r12 /* restore CR */ + mr %r3,%r4 /* get return value */ + blr /* return */ +#if _CALL_ELF == 2 + .size longjmp,.-longjmp +#else + .size longjmp,.-.longjmp +#endif diff --git a/usr/klibc/arch/ppc64/syscall.c b/usr/klibc/arch/ppc64/syscall.c new file mode 100644 index 0000000..a5895fe --- /dev/null +++ b/usr/klibc/arch/ppc64/syscall.c @@ -0,0 +1,14 @@ +/* + * arch/ppc64/syscall.c + * + * Common error-handling path for system calls. + * The return value from __syscall_error becomes the + * return value from the system call. + */ +#include <errno.h> + +long int __syscall_error(long int err) +{ + errno = err; + return -1; +} diff --git a/usr/klibc/arch/ppc64/sysstub.ph b/usr/klibc/arch/ppc64/sysstub.ph new file mode 100644 index 0000000..a0c6d41 --- /dev/null +++ b/usr/klibc/arch/ppc64/sysstub.ph @@ -0,0 +1,47 @@ +# -*- perl -*- +# +# arch/ppc64/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT <<EOF; +#include <asm/unistd.h> + + .text + .balign 4 + .globl ${fname} +#if _CALL_ELF == 2 + .type ${fname},\@function +${fname}: +0: addis 2,12,(.TOC.-0b)\@ha + addi 2,2,(.TOC.-0b)\@l + .localentry ${fname},.-${fname} +#else + .section ".opd","aw" + .balign 8 +${fname}: + .quad .${fname}, .TOC.\@tocbase, 0 + .previous + .type .${fname},\@function + .globl .${fname} +.${fname}: +#endif + li 0, __NR_${sname} + sc + bnslr + b __syscall_error +#if _CALL_ELF == 2 + .size ${fname},.-${fname} +#else + .size ${fname},.-.${fname} +#endif +EOF + close(OUT); +} + +1; diff --git a/usr/klibc/arch/riscv64/Kbuild b/usr/klibc/arch/riscv64/Kbuild new file mode 100644 index 0000000..218f386 --- /dev/null +++ b/usr/klibc/arch/riscv64/Kbuild @@ -0,0 +1,12 @@ +# -*- makefile -*- +# +# klibc files for riscv64 + +always := crt0.o _main.o +targets := crt0.o _main.o + +klib-y := setjmp.o syscall.o + +install-rule: + $(Q)$(shell $(install-data) $(call objectify,_main.o) \ + $(INSTALLROOT)$(INSTALLDIR)/$(KLIBCCROSS)lib) diff --git a/usr/klibc/arch/riscv64/MCONFIG b/usr/klibc/arch/riscv64/MCONFIG new file mode 100644 index 0000000..717aeda --- /dev/null +++ b/usr/klibc/arch/riscv64/MCONFIG @@ -0,0 +1,29 @@ +# -*- makefile -*- +# +# arch/riscv64/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCOPTFLAGS += -Os -fomit-frame-pointer +ifeq ($(DEBUG),y) +KLIBCOPTFLAGS += -g +endif +KLIBCBITSIZE = 64 + +# Normal binaries start at 64 kiB. Jumps can use either a single +# instruction with offset of ±1 MiB, or two instructions with offset +# of ±2 GiB. Putting klibc.so close above the executable can cause +# breakage, so instead swap them around: klibc.so at 64 kiB and +# executable at 576 kiB. +KLIBCLDFLAGS = $(LD_IMAGE_BASE_OPT) 0x90000 +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x10000 +KLIBCSHAREDFLAGS += --defsym '__global_pointer$$=0' + +# Kernel has never used stack trampolines +KLIBCEXECSTACK := n + +KLIBCEMAIN := -e _main +KLIBCCRTSHARED += $(KLIBCOBJ)/arch/riscv64/_main.o diff --git a/usr/klibc/arch/riscv64/_main.S b/usr/klibc/arch/riscv64/_main.S new file mode 100644 index 0000000..284a222 --- /dev/null +++ b/usr/klibc/arch/riscv64/_main.S @@ -0,0 +1,17 @@ +# +# arch/riscv64/_main.S +# +# Does arch-specific initialization and invokes main with the +# appropriate arguments. +# + +#include <machine/asm.h> + +ENTRY(_main) + .option push + .option norelax + lla gp, __global_pointer$ + .option pop + + j main +END(_main) diff --git a/usr/klibc/arch/riscv64/crt0.S b/usr/klibc/arch/riscv64/crt0.S new file mode 100644 index 0000000..76fa3c2 --- /dev/null +++ b/usr/klibc/arch/riscv64/crt0.S @@ -0,0 +1,22 @@ +# +# arch/riscv64/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + +#include <machine/asm.h> + +ENTRY(_start) + .option push + .option norelax + lla gp, __global_pointer$ + .option pop + + mv a0, sp # Pointer to ELF entry structure + mv a1, zero # No onexit pointer + jal __libc_init +END(_start) diff --git a/usr/klibc/arch/riscv64/setjmp.S b/usr/klibc/arch/riscv64/setjmp.S new file mode 100644 index 0000000..66e009e --- /dev/null +++ b/usr/klibc/arch/riscv64/setjmp.S @@ -0,0 +1,50 @@ +/* + * arch/riscv64/setjmp.S + * + * setjmp/longjmp for the RISC-V (RV64) architecture + * + * The jmp_buf is assumed to contain the following, in order: + * pc (ra) + * s0..s11 + * sp + */ + +#include <machine/asm.h> + +ENTRY(setjmp) + sd ra, 0(a0) + sd s0, 8(a0) + sd s1, 16(a0) + sd s2, 24(a0) + sd s3, 32(a0) + sd s4, 40(a0) + sd s5, 48(a0) + sd s6, 56(a0) + sd s7, 64(a0) + sd s8, 72(a0) + sd s9, 80(a0) + sd s10, 88(a0) + sd s11, 96(a0) + sd sp, 104(a0) + mv a0, zero + jr ra +END(setjmp) + +ENTRY(longjmp) + ld ra, 0(a0) + ld s0, 8(a0) + ld s1, 16(a0) + ld s2, 24(a0) + ld s3, 32(a0) + ld s4, 40(a0) + ld s5, 48(a0) + ld s6, 56(a0) + ld s7, 64(a0) + ld s8, 72(a0) + ld s9, 80(a0) + ld s10, 88(a0) + ld s11, 96(a0) + ld sp, 104(a0) + mv a0, a1 + jr ra +END(longjmp) diff --git a/usr/klibc/arch/riscv64/syscall.S b/usr/klibc/arch/riscv64/syscall.S new file mode 100644 index 0000000..89d9bd2 --- /dev/null +++ b/usr/klibc/arch/riscv64/syscall.S @@ -0,0 +1,13 @@ +#include <machine/asm.h> +#include <asm/unistd.h> + +ENTRY(__syscall_common) + scall + li t0, -4096 + bleu a0, t0, 1f + neg a0, a0 + lui t0, %hi(errno) + sw a0, %lo(errno)(t0) + li a0, -1 +1: jr ra +END(__syscall_common) diff --git a/usr/klibc/arch/riscv64/sysstub.ph b/usr/klibc/arch/riscv64/sysstub.ph new file mode 100644 index 0000000..85c1beb --- /dev/null +++ b/usr/klibc/arch/riscv64/sysstub.ph @@ -0,0 +1,26 @@ +# -*- perl -*- +# +# arch/riscv/sysstub.ph +# +# Script to generate system call stubs +# + +# On RISC-V, most system calls follow the standard convention, with the +# system call number in x17 (a7) and the return value in x10 (a0). + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + $stype = $stype || 'common'; + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <machine/asm.h>\n"; + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "ENTRY(${fname})\n"; + print OUT "\tli\ta7, __NR_${sname}\n"; + print OUT "\tj\t__syscall_${stype}\n"; + print OUT "END(${fname})\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/s390/Kbuild b/usr/klibc/arch/s390/Kbuild new file mode 100644 index 0000000..6a8e97a --- /dev/null +++ b/usr/klibc/arch/s390/Kbuild @@ -0,0 +1,22 @@ +# +# klibc files for s390 +# + +always := crt0.o +targets := crt0.o + +ifneq ("$(KLIBCARCH)", "s390x") + +klib-y := setjmp.o mmap.o syscall.o + +klib-y += ../../libgcc/__clzsi2.o ../../libgcc/__ashldi3.o +klib-y += ../../libgcc/__ashrdi3.o ../../libgcc/__lshrdi3.o +klib-y += ../../libgcc/__divdi3.o ../../libgcc/__moddi3.o +klib-y += ../../libgcc/__udivdi3.o ../../libgcc/__umoddi3.o +klib-y += ../../libgcc/__udivmoddi4.o + +else + +klib-y := setjmp.o mmap.o syscall.o + +endif diff --git a/usr/klibc/arch/s390/MCONFIG b/usr/klibc/arch/s390/MCONFIG new file mode 100644 index 0000000..a704c5a --- /dev/null +++ b/usr/klibc/arch/s390/MCONFIG @@ -0,0 +1,26 @@ +# -*- makefile -*- +# +# arch/s390/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCOPTFLAGS += -Os + +ifneq ("$(KLIBCARCH)", "s390x") + KLIBCBITSIZE = 32 + KLIBCCFLAGS += -m31 + KLIBCLDFLAGS += -m elf_s390 +else + KLIBCBITSIZE = 64 + KLIBCCFLAGS += -m64 + KLIBCLDFLAGS += -m elf64_s390 +endif + +KLIBCASMARCH = s390 +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x40000000 + +# Kernel uses our sa_restorer for signal return +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/s390/crt0.S b/usr/klibc/arch/s390/crt0.S new file mode 100644 index 0000000..fd9237e --- /dev/null +++ b/usr/klibc/arch/s390/crt0.S @@ -0,0 +1,35 @@ +# +# arch/s390/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + .text + .align 4 + .type _start,@function + .globl _start + +#ifndef __s390x__ + +_start: + lr %r2,%r15 + lhi %r3,0 + ahi %r15,-96 + bras %r1,.L0 +.L0: + l %r1,.L1-.L0(%r1) + br %r1 +.L1: + .long __libc_init +#else + +_start: + lgr %r2,%r15 + lghi %r3,0 + aghi %r15,-160 + jg __libc_init +#endif + .size _start,.-_start diff --git a/usr/klibc/arch/s390/mmap.c b/usr/klibc/arch/s390/mmap.c new file mode 100644 index 0000000..dfc5a84 --- /dev/null +++ b/usr/klibc/arch/s390/mmap.c @@ -0,0 +1,74 @@ +#include <errno.h> +#include <sys/types.h> +#include <linux/unistd.h> + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +#ifndef __s390x__ + +void *__mmap2(void *addr, size_t len, int prot, int flags, int fd, long offset) +{ + struct mmap_arg_struct args = { + .addr = (unsigned long)addr, + .len = (unsigned long)len, + .prot = (unsigned long)prot, + .flags = (unsigned long)flags, + .fd = (unsigned long)fd, + .offset = (unsigned long)offset, + }; + + register struct mmap_arg_struct *__arg1 asm("2") = &args; + register long __svcres asm("2"); + unsigned long __res; + + __asm__ __volatile__(" svc %b1\n" + : "=d"(__svcres) + : "i"(__NR_mmap2), "0"(__arg1) + : "1", "cc", "memory"); + __res = __svcres; + if (__res >= (unsigned long)-4095) { + errno = -__res; + __res = -1; + } + return (void *)__res; +} + +#else /* __s390x__ */ + +void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + struct mmap_arg_struct args = { + .addr = (unsigned long)addr, + .len = (unsigned long)len, + .prot = (unsigned long)prot, + .flags = (unsigned long)flags, + .fd = (unsigned long)fd, + .offset = (unsigned long)offset, + }; + + register struct mmap_arg_struct *__arg1 asm("2") = &args; + register long __svcres asm("2"); + unsigned long __res; + + __asm__ __volatile__ ( + " svc %1\n" + : "=d" (__svcres) + : "i" (__NR_mmap), + "0" (__arg1) + : "1", "cc", "memory"); + __res = __svcres; + if (__res >= (unsigned long)-4095) { + errno = -__res; + __res = -1; + } + return (void *)__res; +} + +#endif /* __s390x__ */ diff --git a/usr/klibc/arch/s390/setjmp.S b/usr/klibc/arch/s390/setjmp.S new file mode 100644 index 0000000..1386419 --- /dev/null +++ b/usr/klibc/arch/s390/setjmp.S @@ -0,0 +1,74 @@ +# +# arch/s390/setjmp.S +# +# setjmp/longjmp for the s390 architecture +# + + .text + .align 4 + .globl setjmp + .type setjmp, @function + +#ifndef __s390x__ + +setjmp: + stm %r6,%r15,0(%r2) # save all general registers + std %f4,40(%r2) # save fp registers f4 and f6 + std %f6,48(%r2) + lhi %r2,0 # return 0 + br %r14 + + .size setjmp,.-setjmp + + .text + .align 4 + .globl longjmp + .type longjmp, @function +longjmp: + lr %r1,%r2 # jmp_buf + lr %r2,%r3 # return value + ld %f6,48(%r1) # restore all saved registers + ld %f4,40(%r1) + lm %r6,%r15,0(%r1) + br %r14 # return to restored address + + .size longjmp,.-longjmp + +#else + +setjmp: + stmg %r6,%r15,0(%r2) # save all general registers + std %f8,80(%r2) # save fp registers f8 to f15 + std %f9,88(%r2) + std %f10,96(%r2) + std %f11,104(%r2) + std %f12,112(%r2) + std %f13,120(%r2) + std %f14,128(%r2) + std %f15,136(%r2) + lghi %r2,0 # return 0 + br %r14 + + .size setjmp,.-setjmp + + .text + .align 4 + .globl longjmp + .type longjmp, @function +longjmp: + lgr %r1,%r2 # jmp_buf + lgr %r2,%r3 # return value + ld %f15,136(%r1) # restore all saved registers + ld %f14,128(%r1) + ld %f13,120(%r1) + ld %f12,112(%r1) + ld %f11,104(%r1) + ld %f10,96(%r1) + ld %f9,88(%r1) + ld %f8,80(%r1) + lmg %r6,%r15,0(%r1) + br %r14 # return to restored address + + .size longjmp,.-longjmp + +#endif diff --git a/usr/klibc/arch/s390/syscall.c b/usr/klibc/arch/s390/syscall.c new file mode 100644 index 0000000..e1d201d --- /dev/null +++ b/usr/klibc/arch/s390/syscall.c @@ -0,0 +1,16 @@ +/* + * arch/s390/syscall.c + * + * Common error-handling path for system calls. + * The return value from __syscall_common becomes the + * return value from the system call. + */ +#include <errno.h> + +unsigned long __syscall_common(unsigned long err) +{ + if (err < -4095UL) + return err; + errno = -err; + return -1; +} diff --git a/usr/klibc/arch/s390/sysstub.ph b/usr/klibc/arch/s390/sysstub.ph new file mode 100644 index 0000000..1ca7b6b --- /dev/null +++ b/usr/klibc/arch/s390/sysstub.ph @@ -0,0 +1,63 @@ +# -*- perl -*- +# +# arch/s390/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + my($t); + my($r, $llregs) = (0, ($typesize{'void *'} == 8) ? 1 : 2); + + foreach $t (@args) { + $r += ($typesize{$t} == 8) ? $llregs : 1; + } + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT <<EOF; +#include <asm/unistd.h> + + .type ${fname},\@function + .globl ${fname} +${fname}: +.if ${r} > 6 +.print "System call with more than six parameters not supported yet." +.err +.endif +.if ${r} == 6 +#ifndef __s390x__ + st %r7,56(%r15) + l %r7,96(%r15) +#else + stg %r7,80(%r15) + lg %r7,160(%r15) +#endif +.endif +.if __NR_${sname} < 256 + svc __NR_${sname} +.else + la %r1,__NR_${sname} + svc 0 +.endif +.if ${r} == 6 +#ifndef __s390x__ + l %r7,56(%r15) +#else + lg %r7,160(%r15) +#endif +.endif +#ifndef __s390x__ + bras %r3,1f + .long __syscall_common +1: l %r3,0(%r3) + br %r3 +#else + brasl %r3,__syscall_common +#endif + .size ${fname},.-${fname} +EOF + close(OUT); +} + +1; diff --git a/usr/klibc/arch/sh/Kbuild b/usr/klibc/arch/sh/Kbuild new file mode 100644 index 0000000..29b606a --- /dev/null +++ b/usr/klibc/arch/sh/Kbuild @@ -0,0 +1,8 @@ +# +# klibc files for sh +# + +klib-y := pipe.o setjmp.o syscall.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/sh/MCONFIG b/usr/klibc/arch/sh/MCONFIG new file mode 100644 index 0000000..43d608c --- /dev/null +++ b/usr/klibc/arch/sh/MCONFIG @@ -0,0 +1,20 @@ +# -*- makefile -*- +# +# arch/sh/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCOPTFLAGS += -Os -fomit-frame-pointer +KLIBCBITSIZE = 32 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 2 MB -- the normal starting point for text is 4 MB. +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x00200000 + +# Kernel uses vDSO for signal return since 2.6.19 +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/sh/crt0.S b/usr/klibc/arch/sh/crt0.S new file mode 100644 index 0000000..7f0a649 --- /dev/null +++ b/usr/klibc/arch/sh/crt0.S @@ -0,0 +1,27 @@ +# +# arch/sh/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + + .text + .align 2 + .type _start,#function + .globl _start + +_start: + mov r15, r4 + mov #0, r5 + mov.l 1f, r0 + + jsr @r0 + nop + + .align 2 +1: .long __libc_init + + .size _start,.-_start diff --git a/usr/klibc/arch/sh/pipe.S b/usr/klibc/arch/sh/pipe.S new file mode 100644 index 0000000..01b055b --- /dev/null +++ b/usr/klibc/arch/sh/pipe.S @@ -0,0 +1,35 @@ +/* + * arch/sh/pipe.S + * + * The pipe system call is special on sh: it returns + * the two file descriptors in r0 and r1. + */ + +#include <asm/unistd.h> + + .section ".text.syscall","ax" + .align 2 + .globl pipe + .type pipe,@function +pipe: + mov #__NR_pipe, r3 + trapa #0x10 + mov.l 1f,r2 + cmp/hs r0,r2 + bt/s 3f + neg r0,r2 + mov.l 2f,r3 + mov.l r2,@r3 + rts + mov #-1,r0 +3: + mov.l r0, @r4 + mov.l r1, @(4, r4) + rts + mov #0,r0 + + .align 2 +1: .long -4096 /* Errno limit */ +2: .long errno + + .size pipe,.-pipe diff --git a/usr/klibc/arch/sh/setjmp.S b/usr/klibc/arch/sh/setjmp.S new file mode 100644 index 0000000..2552358 --- /dev/null +++ b/usr/klibc/arch/sh/setjmp.S @@ -0,0 +1,64 @@ +# +# arch/sh/setjmp.S +# +# setjmp/longjmp for the SuperH architecture +# + +# +# The jmp_buf is assumed to contain the following, in order: +# +# r8 +# r9 +# r10 +# r11 +# r12 +# r13 +# r14 +# r15 +# pr +# + + .text + .align 2 + + .globl setjmp + .type setjmp, #function + +setjmp: + add #(9*4), r4 + sts.l pr, @-r4 + mov.l r15, @-r4 + mov.l r14, @-r4 + mov.l r13, @-r4 + mov.l r12, @-r4 + mov.l r11, @-r4 + mov.l r10, @-r4 + mov.l r9, @-r4 + mov.l r8, @-r4 + rts + mov #0, r0 + + .size setjmp,.-setjmp + + .align 2 + .globl longjmp + .type setjmp, #function + +longjmp: + mov.l @r4+, r8 + mov.l @r4+, r9 + mov.l @r4+, r10 + mov.l @r4+, r11 + mov.l @r4+, r12 + mov.l @r4+, r13 + mov.l @r4+, r14 + mov.l @r4+, r15 + lds.l @r4+, pr + mov r5, r0 + tst r0, r0 + bf 1f + mov #1, r0 ! in case val==0 +1: rts + nop + + .size longjmp,.-longjmp diff --git a/usr/klibc/arch/sh/syscall.S b/usr/klibc/arch/sh/syscall.S new file mode 100644 index 0000000..77245b7 --- /dev/null +++ b/usr/klibc/arch/sh/syscall.S @@ -0,0 +1,35 @@ +/* + * arch/sh/syscall.S + * + * On sh, r3 contains the syscall number (set by generated stub); + * r4..r7 contain arguments 0-3 per the standard calling convention, + * and arguments 4-5 are passed in r0 and r1. + * + * The return value is in r0. + */ + + .section ".text.syscall","ax" + .align 2 + .globl __syscall_common + .type __syscall_common,@function +__syscall_common: + mov.l @(0,sp),r0 + mov.l @(4,sp),r1 + trapa #0x15 + mov.l 1f,r1 + cmp/hs r0,r1 + bt/s 3f + neg r0,r1 + mov.l 2f,r2 + mov.l r1,@r2 + rts + mov #-1,r0 +3: + rts + nop + + .align 2 +1: .long -4096 /* Errno limit */ +2: .long errno + + .size __syscall_common,.-__syscall_common diff --git a/usr/klibc/arch/sh/sysstub.ph b/usr/klibc/arch/sh/sysstub.ph new file mode 100644 index 0000000..0ff46dd --- /dev/null +++ b/usr/klibc/arch/sh/sysstub.ph @@ -0,0 +1,38 @@ +# -*- perl -*- +# +# arch/sh/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.section\t\".text.syscall\",\"ax\"\n"; + print OUT "\t.type\t${fname},\#function\n"; + print OUT "\t.globl\t${fname}\n"; + print OUT "\t.align\t2\n"; + print OUT "\t.import __syscall_common\n"; + print OUT "${fname}:\n"; + print OUT "\t mov.l\t1f, r3\n"; + print OUT "\t jmp\t\@r3\n"; + print OUT "#if __NR_${sname} >= 128\n"; + print OUT "\t mov.l\t2f, r3\n"; + print OUT "#else\n"; + print OUT "\t mov\t# __NR_${sname}, r3\n"; + print OUT "#endif\n"; + print OUT "\t.size ${fname},.-${fname}\n"; + print OUT "\n"; + print OUT "\t.align\t2\n"; + print OUT "\t.import\t__syscall_common\n"; + print OUT "1:\t.long\t__syscall_common\n"; + print OUT "#if __NR_${sname} >= 128\n"; + print OUT "2:\t.long\t__NR_${sname}\n"; + print OUT "#endif\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/sparc/Kbuild b/usr/klibc/arch/sparc/Kbuild new file mode 100644 index 0000000..d92bbb0 --- /dev/null +++ b/usr/klibc/arch/sparc/Kbuild @@ -0,0 +1,34 @@ +# +# klibc files for sparc +# + +always := crt0.o +targets := crt0.o + +m4-targets := sdiv.o srem.o udiv.o urem.o + +klib-y := $(m4-targets) smul.o umul.o __muldi3.o +klib-y += setjmp.o pipe.o sigaction.o syscall.o sysfork.o + +klib-y += ../../libgcc/__ashldi3.o ../../libgcc/__ashrdi3.o +klib-y += ../../libgcc/__lshrdi3.o ../../libgcc/__divdi3.o +klib-y += ../../libgcc/__moddi3.o ../../libgcc/__udivdi3.o +klib-y += ../../libgcc/__umoddi3.o ../../libgcc/__udivmoddi4.o +klib-y += ../../libgcc/__clzsi2.o + + +$(obj)/sdiv.S: m4 := define(NAME,\`.div')define(OP,\`div')define(S,\`true') +$(obj)/srem.S: m4 := define(NAME,\`.rem')define(OP,\`rem')define(S,\`true') +$(obj)/udiv.S: m4 := define(NAME,\`.udiv')define(OP,\`div')define(S,\`false') +$(obj)/urem.S: m4 := define(NAME,\`.urem')define(OP,\`rem')define(S,\`false') + +targets += $(m4-targets) $(m4-targets:.o=.S) + +quiet_cmd_m4 = M4 $@ + cmd_m4 = (echo "$(m4)"; cat $^) | m4 > $@ + +# build .o from .S +$(addprefix $(obj)/,$(m4-targets)): $(obj)/%.o : $(obj)/%.S +# build .S from .m4 +$(addprefix $(obj)/,$(m4-targets:.o=.S)): $(src)/divrem.m4 + $(call if_changed,m4) diff --git a/usr/klibc/arch/sparc/MCONFIG b/usr/klibc/arch/sparc/MCONFIG new file mode 100644 index 0000000..623223d --- /dev/null +++ b/usr/klibc/arch/sparc/MCONFIG @@ -0,0 +1,22 @@ +# -*- makefile -*- +# +# arch/sparc/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCOPTFLAGS += -Os -m32 -mptr32 +KLIBCBITSIZE = 32 +KLIBCARCHREQFLAGS += -D__sparc32__ + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# Normal binaries start at 64K; the linker wants 64K alignment, +# and call instructions have a 30-bit signed offset, << 2. +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x40000000 + +# Kernel uses our sa_restorer for signal return +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/sparc/__muldi3.S b/usr/klibc/arch/sparc/__muldi3.S new file mode 100644 index 0000000..e53848a --- /dev/null +++ b/usr/klibc/arch/sparc/__muldi3.S @@ -0,0 +1,27 @@ + .global .umul + .section ".text" + .align 4 + .global __muldi3 + .type __muldi3, #function + .proc 017 +__muldi3: + save %sp, -104, %sp + mov %i1, %o0 + call .umul, 0 + mov %i3, %o1 + mov %o0, %l2 + mov %o1, %l3 + mov %i1, %o0 + call .umul, 0 + mov %i2, %o1 + mov %i0, %o1 + mov %o0, %l0 + call .umul, 0 + mov %i3, %o0 + mov 0, %l1 + add %l0, %o0, %l0 + addcc %l3, %l1, %i1 + addx %l2, %l0, %i0 + jmp %i7+8 + restore + .size __muldi3, .-__muldi3 diff --git a/usr/klibc/arch/sparc/crt0.S b/usr/klibc/arch/sparc/crt0.S new file mode 100644 index 0000000..63db188 --- /dev/null +++ b/usr/klibc/arch/sparc/crt0.S @@ -0,0 +1,2 @@ +#define TARGET_PTR_SIZE 32 +#include "crt0i.S" diff --git a/usr/klibc/arch/sparc/crt0i.S b/usr/klibc/arch/sparc/crt0i.S new file mode 100644 index 0000000..0220b01 --- /dev/null +++ b/usr/klibc/arch/sparc/crt0i.S @@ -0,0 +1,100 @@ +! This file derived from the equivalent in newlib +! +! C run time start off + +! This file supports: +! +! - both 32bit pointer and 64bit pointer environments (at compile time) +! - an imposed stack bias (of 2047) (at run time) +! - medium/low and medium/anywhere code models (at run time) + +! Initial stack setup: +! +! bottom of stack (higher memory address) +! ... +! text of environment strings +! text of argument strings +! envp[envc] = 0 (4/8 bytes) +! ... +! env[0] (4/8 bytes) +! argv[argc] = 0 (4/8 bytes) +! ... +! argv[0] (4/8 bytes) +! argc (4/8 bytes) +! register save area (64 bits by 16 registers = 128 bytes) +! top of stack (%sp) + +! Stack Bias: +! +! It is the responsibility of the o/s to set this up. +! We handle both a 0 and 2047 value for the stack bias. + +! Medium/Anywhere code model support: +! +! In this model %g4 points to the start of the data segment. +! The text segment can go anywhere, but %g4 points to the *data* segment. +! It is up to the compiler/linker to get this right. +! +! Since this model is statically linked the start of the data segment +! is known at link time. Eg: +! +! sethi %hh(data_start), %g1 +! sethi %lm(data_start), %g4 +! or %g1, %hm(data_start), %g1 +! or %g4, %lo(data_start), %g4 +! sllx %g1, 32, %g1 +! or %g4, %g1, %g4 +! +! FIXME: For now we just assume 0. + +! FIXME: if %g1 contains a non-zero value, atexit() should be invoked +! with this value. + + + .text + .align 4 + .globl _start + .type _start, @function +_start: + clr %fp + +! We use %g4 even if the code model is Medium/Low (simplifies the code). + + clr %g4 ! Medium/Anywhere base reg + +! If there is a stack bias in effect, account for it in %g5. Then always +! add %g5 to stack references below. This way the code can be used with +! or without an imposed bias. + + andcc %sp, 1, %g5 + bz,a .LNoBias + nop + mov 2047, %g5 +.LNoBias: + add %sp, %g5, %g5 + +! On entry, the kernel leaves room for one register frame, but +! the C API wants more free space. Thus, we need to drop the stack +! pointer additionally. + +#if TARGET_PTR_SIZE == 32 + sub %sp, 32, %sp ! make room for incoming arguments +#else /* TARGET_PTR_SIZE == 64 */ + sub %sp, 64, %sp ! make room for incoming arguments +#endif + +! Set up pointers to the ELF data structure (argc, argv, ...) +! Pass as the first argument to __libc_init +#if TARGET_PTR_SIZE == 32 + add %g5, 0x40, %o0 +#else /* TARGET_PTR_SIZE == 64 */ + add %g5, 0x80, %o0 +#endif + + call __libc_init + mov %g1, %o1 ! This is the "atexit" pointer; + ! pass as the second argument to __libc_init + +! If __libc_init returns, something is hosed. Try an illegal insn. +! If that does not work, the o/s is hosed more than we are. + .long 0 diff --git a/usr/klibc/arch/sparc/divrem.m4 b/usr/klibc/arch/sparc/divrem.m4 new file mode 100644 index 0000000..aa4171d --- /dev/null +++ b/usr/klibc/arch/sparc/divrem.m4 @@ -0,0 +1,276 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: Header: divrem.m4,v 1.4 92/06/25 13:23:57 torek Exp + * $NetBSD: divrem.m4,v 1.4 1997/10/09 10:07:54 lukem Exp $ + */ + +/* + * Division and remainder, from Appendix E of the Sparc Version 8 + * Architecture Manual, with fixes from Gordon Irlam. + */ + +#if defined(LIBC_SCCS) && !defined(lint) + .asciz "@(#)divrem.m4 8.1 (Berkeley) 6/4/93" +#endif /* LIBC_SCCS and not lint */ + +/* + * Input: dividend and divisor in %o0 and %o1 respectively. + * + * m4 parameters: + * NAME name of function to generate + * OP OP=div => %o0 / %o1; OP=rem => %o0 % %o1 + * S S=true => signed; S=false => unsigned + * + * Algorithm parameters: + * N how many bits per iteration we try to get (4) + * WORDSIZE total number of bits (32) + * + * Derived constants: + * TWOSUPN 2^N, for label generation (m4 exponentiation currently broken) + * TOPBITS number of bits in the top `decade' of a number + * + * Important variables: + * Q the partial quotient under development (initially 0) + * R the remainder so far, initially the dividend + * ITER number of main division loop iterations required; + * equal to ceil(log2(quotient) / N). Note that this + * is the log base (2^N) of the quotient. + * V the current comparand, initially divisor*2^(ITER*N-1) + * + * Cost: + * Current estimate for non-large dividend is + * ceil(log2(quotient) / N) * (10 + 7N/2) + C + * A large dividend is one greater than 2^(31-TOPBITS) and takes a + * different path, as the upper bits of the quotient must be developed + * one bit at a time. + */ + +define(N, `4') +define(TWOSUPN, `16') +define(WORDSIZE, `32') +define(TOPBITS, eval(WORDSIZE - N*((WORDSIZE-1)/N))) + +define(dividend, `%o0') +define(divisor, `%o1') +define(Q, `%o2') +define(R, `%o3') +define(ITER, `%o4') +define(V, `%o5') + +/* m4 reminder: ifelse(a,b,c,d) => if a is b, then c, else d */ +define(T, `%g1') +define(SC, `%g7') +ifelse(S, `true', `define(SIGN, `%g6')') + +/* + * This is the recursive definition for developing quotient digits. + * + * Parameters: + * $1 the current depth, 1 <= $1 <= N + * $2 the current accumulation of quotient bits + * N max depth + * + * We add a new bit to $2 and either recurse or insert the bits in + * the quotient. R, Q, and V are inputs and outputs as defined above; + * the condition codes are expected to reflect the input R, and are + * modified to reflect the output R. + */ +define(DEVELOP_QUOTIENT_BITS, +` ! depth $1, accumulated bits $2 + bl L.$1.eval(TWOSUPN+$2) + srl V,1,V + ! remainder is positive + subcc R,V,R + ifelse($1, N, + ` b 9f + add Q, ($2*2+1), Q + ', ` DEVELOP_QUOTIENT_BITS(incr($1), `eval(2*$2+1)')') +L.$1.eval(TWOSUPN+$2): + ! remainder is negative + addcc R,V,R + ifelse($1, N, + ` b 9f + add Q, ($2*2-1), Q + ', ` DEVELOP_QUOTIENT_BITS(incr($1), `eval(2*$2-1)')') + ifelse($1, 1, `9:')') + +#include <machine/asm.h> +#include <machine/trap.h> + +FUNC(NAME) +ifelse(S, `true', +` ! compute sign of result; if neither is negative, no problem + orcc divisor, dividend, %g0 ! either negative? + bge 2f ! no, go do the divide + ifelse(OP, `div', + `xor divisor, dividend, SIGN', + `mov dividend, SIGN') ! compute sign in any case + tst divisor + bge 1f + tst dividend + ! divisor is definitely negative; dividend might also be negative + bge 2f ! if dividend not negative... + neg divisor ! in any case, make divisor nonneg +1: ! dividend is negative, divisor is nonnegative + neg dividend ! make dividend nonnegative +2: +') + ! Ready to divide. Compute size of quotient; scale comparand. + orcc divisor, %g0, V + bnz 1f + mov dividend, R + + ! Divide by zero trap. If it returns, return 0 (about as + ! wrong as possible, but that is what SunOS does...). + t ST_DIV0 + retl + clr %o0 + +1: + cmp R, V ! if divisor exceeds dividend, done + blu Lgot_result ! (and algorithm fails otherwise) + clr Q + sethi %hi(1 << (WORDSIZE - TOPBITS - 1)), T + cmp R, T + blu Lnot_really_big + clr ITER + + ! `Here the dividend is >= 2^(31-N) or so. We must be careful here, + ! as our usual N-at-a-shot divide step will cause overflow and havoc. + ! The number of bits in the result here is N*ITER+SC, where SC <= N. + ! Compute ITER in an unorthodox manner: know we need to shift V into + ! the top decade: so do not even bother to compare to R.' + 1: + cmp V, T + bgeu 3f + mov 1, SC + sll V, N, V + b 1b + inc ITER + + ! Now compute SC. + 2: addcc V, V, V + bcc Lnot_too_big + inc SC + + ! We get here if the divisor overflowed while shifting. + ! This means that R has the high-order bit set. + ! Restore V and subtract from R. + sll T, TOPBITS, T ! high order bit + srl V, 1, V ! rest of V + add V, T, V + b Ldo_single_div + dec SC + + Lnot_too_big: + 3: cmp V, R + blu 2b + nop + be Ldo_single_div + nop + /* NB: these are commented out in the V8-Sparc manual as well */ + /* (I do not understand this) */ + ! V > R: went too far: back up 1 step + ! srl V, 1, V + ! dec SC + ! do single-bit divide steps + ! + ! We have to be careful here. We know that R >= V, so we can do the + ! first divide step without thinking. BUT, the others are conditional, + ! and are only done if R >= 0. Because both R and V may have the high- + ! order bit set in the first step, just falling into the regular + ! division loop will mess up the first time around. + ! So we unroll slightly... + Ldo_single_div: + deccc SC + bl Lend_regular_divide + nop + sub R, V, R + mov 1, Q + b Lend_single_divloop + nop + Lsingle_divloop: + sll Q, 1, Q + bl 1f + srl V, 1, V + ! R >= 0 + sub R, V, R + b 2f + inc Q + 1: ! R < 0 + add R, V, R + dec Q + 2: + Lend_single_divloop: + deccc SC + bge Lsingle_divloop + tst R + b,a Lend_regular_divide + +Lnot_really_big: +1: + sll V, N, V + cmp V, R + bleu 1b + inccc ITER + be Lgot_result + dec ITER + + tst R ! set up for initial iteration +Ldivloop: + sll Q, N, Q + DEVELOP_QUOTIENT_BITS(1, 0) +Lend_regular_divide: + deccc ITER + bge Ldivloop + tst R + bl,a Lgot_result + ! non-restoring fixup here (one instruction only!) +ifelse(OP, `div', +` dec Q +', ` add R, divisor, R +') + +Lgot_result: +ifelse(S, `true', +` ! check to see if answer should be < 0 + tst SIGN + bl,a 1f + ifelse(OP, `div', `neg Q', `neg R') +1:') + retl + ifelse(OP, `div', `mov Q, %o0', `mov R, %o0') diff --git a/usr/klibc/arch/sparc/pipe.S b/usr/klibc/arch/sparc/pipe.S new file mode 100644 index 0000000..e278bda --- /dev/null +++ b/usr/klibc/arch/sparc/pipe.S @@ -0,0 +1,31 @@ +/* + * arch/sparc/pipe.S + * + * The pipe system call are special on sparc[64]: + * they return the two file descriptors in %o0 and %o1. + */ + +#include <machine/asm.h> +#include <asm/unistd.h> + + .globl pipe + .type pipe,#function + .align 4 +pipe: + mov __NR_pipe, %g1 + or %o0, 0, %g4 + t 0x10 + bcc 1f + nop + PIC_PROLOGUE(%g1,%g4) + SET(errno,%g1,%g4) + st %o0,[%g4] + retl + mov -1, %o0 +1: + st %o0,[%g4] + st %o1,[%g4+4] + retl + mov 0, %o0 + + .size pipe,.-pipe diff --git a/usr/klibc/arch/sparc/setjmp.S b/usr/klibc/arch/sparc/setjmp.S new file mode 100644 index 0000000..038ea78 --- /dev/null +++ b/usr/klibc/arch/sparc/setjmp.S @@ -0,0 +1,38 @@ +! +! setjmp.S +! +! Basic setjmp/longjmp +! +! This code was based on the equivalent code in NetBSD +! + +#include <machine/asm.h> +#include <machine/trap.h> + +! +! The jmp_buf contains the following entries: +! sp +! fp +! pc +! +ENTRY(setjmp) + st %sp,[%o0+0] ! Callers stack pointer + st %o7,[%o0+4] ! Return pc + st %fp,[%o0+8] ! Frame pointer + retl ! Return + clr %o0 ! ...0 + +ENTRY(longjmp) + sub %sp, 64, %sp ! set up a local stack frame +0: + t ST_FLUSHWIN ! flush register windows out to memory + ! + ! We restore the saved stack pointer to %fp, then issue + ! a restore instruction which will reload the register + ! window from the stack. + ! + ld [%o0+4], %o7 /* restore return pc */ + ld [%o0+0], %fp /* and stack pointer */ + + retl ! success, return %g6 + restore %o1, 0, %o0 diff --git a/usr/klibc/arch/sparc/sigaction.c b/usr/klibc/arch/sparc/sigaction.c new file mode 100644 index 0000000..5c31a52 --- /dev/null +++ b/usr/klibc/arch/sparc/sigaction.c @@ -0,0 +1,21 @@ +/* + * sigaction.c + */ + +#include <signal.h> +#include <sys/syscall.h> + +__extern void __sigreturn(void); +__extern int ____rt_sigaction(int, const struct sigaction *, struct sigaction *, + void (*)(void), size_t); + +int __rt_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact, size_t size) +{ + void (*restorer)(void); + + restorer = (act && act->sa_flags & SA_RESTORER) + ? (void (*)(void))((uintptr_t)act->sa_restorer - 8) + : NULL; + return ____rt_sigaction(sig, act, oact, restorer, size); +} diff --git a/usr/klibc/arch/sparc/smul.S b/usr/klibc/arch/sparc/smul.S new file mode 100644 index 0000000..544ff6e --- /dev/null +++ b/usr/klibc/arch/sparc/smul.S @@ -0,0 +1,160 @@ +/* $NetBSD: mul.S,v 1.3 1997/07/16 14:37:42 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: Header: mul.s,v 1.5 92/06/25 13:24:03 torek Exp + */ + +#include <machine/asm.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 + .asciz "@(#)mul.s 8.1 (Berkeley) 6/4/93" +#else + RCSID("$NetBSD: mul.S,v 1.3 1997/07/16 14:37:42 christos Exp $") +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * Signed multiply, from Appendix E of the Sparc Version 8 + * Architecture Manual. + * + * Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the upper 32 bits of + * the 64-bit product). + * + * This code optimizes short (less than 13-bit) multiplies. + */ + +FUNC(.mul) + mov %o0, %y ! multiplier -> Y + andncc %o0, 0xfff, %g0 ! test bits 12..31 + be Lmul_shortway ! if zero, can do it the short way + andcc %g0, %g0, %o4 ! zero the partial product and clear N and V + + /* + * Long multiply. 32 steps, followed by a final shift step. + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %o1, %o4 ! 13 + mulscc %o4, %o1, %o4 ! 14 + mulscc %o4, %o1, %o4 ! 15 + mulscc %o4, %o1, %o4 ! 16 + mulscc %o4, %o1, %o4 ! 17 + mulscc %o4, %o1, %o4 ! 18 + mulscc %o4, %o1, %o4 ! 19 + mulscc %o4, %o1, %o4 ! 20 + mulscc %o4, %o1, %o4 ! 21 + mulscc %o4, %o1, %o4 ! 22 + mulscc %o4, %o1, %o4 ! 23 + mulscc %o4, %o1, %o4 ! 24 + mulscc %o4, %o1, %o4 ! 25 + mulscc %o4, %o1, %o4 ! 26 + mulscc %o4, %o1, %o4 ! 27 + mulscc %o4, %o1, %o4 ! 28 + mulscc %o4, %o1, %o4 ! 29 + mulscc %o4, %o1, %o4 ! 30 + mulscc %o4, %o1, %o4 ! 31 + mulscc %o4, %o1, %o4 ! 32 + mulscc %o4, %g0, %o4 ! final shift + + ! If %o0 was negative, the result is + ! (%o0 * %o1) + (%o1 << 32)) + ! We fix that here. + + tst %o0 + bge 1f + rd %y, %o0 + + ! %o0 was indeed negative; fix upper 32 bits of result by subtracting + ! %o1 (i.e., return %o4 - %o1 in %o1). + retl + sub %o4, %o1, %o1 + +1: + retl + mov %o4, %o1 + +Lmul_shortway: + /* + * Short multiply. 12 steps, followed by a final shift step. + * The resulting bits are off by 12 and (32-12) = 20 bit positions, + * but there is no problem with %o0 being negative (unlike above). + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %g0, %o4 ! final shift + + /* + * %o4 has 20 of the bits that should be in the low part of the + * result; %y has the bottom 12 (as %y's top 12). That is: + * + * %o4 %y + * +----------------+----------------+ + * | -12- | -20- | -12- | -20- | + * +------(---------+------)---------+ + * --hi-- ----low-part---- + * + * The upper 12 bits of %o4 should be sign-extended to form the + * high part of the product (i.e., highpart = %o4 >> 20). + */ + + rd %y, %o5 + sll %o4, 12, %o0 ! shift middle bits left 12 + srl %o5, 20, %o5 ! shift low bits right 20, zero fill at left + or %o5, %o0, %o0 ! construct low part of result + retl + sra %o4, 20, %o1 ! ... and extract high part of result diff --git a/usr/klibc/arch/sparc/syscall.S b/usr/klibc/arch/sparc/syscall.S new file mode 100644 index 0000000..52a8583 --- /dev/null +++ b/usr/klibc/arch/sparc/syscall.S @@ -0,0 +1,21 @@ +/* + * arch/sparc/syscall.S + * + * Common system-call stub; %g1 already set to syscall number + */ + +#include <machine/asm.h> + + .globl __syscall_common + .type __syscall_common,#function + .align 4 +__syscall_common: + t 0x10 + bcc 1f + PIC_PROLOGUE(%g1,%g4) + SET(errno,%g1,%g4) + st %o0,[%g4] + mov -1, %o0 +1: + retl + nop diff --git a/usr/klibc/arch/sparc/sysfork.S b/usr/klibc/arch/sparc/sysfork.S new file mode 100644 index 0000000..3787b94 --- /dev/null +++ b/usr/klibc/arch/sparc/sysfork.S @@ -0,0 +1,27 @@ +/* + * arch/sparc/sysfork.S + * + * The fork and vfork system calls are special on sparc[64]: + * they return the "other process" pid in %o0 and the + * "is child" flag in %o1 + * + * Common system-call stub; %g1 already set to syscall number + */ + +#include <machine/asm.h> + + .globl __syscall_forkish + .type __syscall_forkish,#function + .align 4 +__syscall_forkish: + t 0x10 + sub %o1, 1, %o1 + bcc,a 1f + and %o0, %o1, %o0 + PIC_PROLOGUE(%g1,%g4) + SET(errno,%g1,%g4) + st %o0,[%g4] + mov -1, %o0 +1: + retl + nop diff --git a/usr/klibc/arch/sparc/sysstub.ph b/usr/klibc/arch/sparc/sysstub.ph new file mode 100644 index 0000000..d8cedb5 --- /dev/null +++ b/usr/klibc/arch/sparc/sysstub.ph @@ -0,0 +1,25 @@ +# -*- perl -*- +# +# arch/sparc32/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + $stype = $stype || 'common'; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.type ${fname},\@function\n"; + print OUT "\t.globl ${fname}\n"; + print OUT "${fname}:\n"; + print OUT "\tb __syscall_${stype}\n"; + print OUT "\t mov\t__NR_${sname}, %g1\n"; + print OUT "\t.size ${fname},.-${fname}\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/sparc/umul.S b/usr/klibc/arch/sparc/umul.S new file mode 100644 index 0000000..6a7193d --- /dev/null +++ b/usr/klibc/arch/sparc/umul.S @@ -0,0 +1,193 @@ +/* $NetBSD: umul.S,v 1.3 1997/07/16 14:37:44 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: Header: umul.s,v 1.4 92/06/25 13:24:05 torek Exp + */ + +#include <machine/asm.h> +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 + .asciz "@(#)umul.s 8.1 (Berkeley) 6/4/93" +#else + RCSID("$NetBSD: umul.S,v 1.3 1997/07/16 14:37:44 christos Exp $") +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * Unsigned multiply. Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the + * upper 32 bits of the 64-bit product). + * + * This code optimizes short (less than 13-bit) multiplies. Short + * multiplies require 25 instruction cycles, and long ones require + * 45 instruction cycles. + * + * On return, overflow has occurred (%o1 is not zero) if and only if + * the Z condition code is clear, allowing, e.g., the following: + * + * call .umul + * nop + * bnz overflow (or tnz) + */ + +FUNC(.umul) + or %o0, %o1, %o4 + mov %o0, %y ! multiplier -> Y + andncc %o4, 0xfff, %g0 ! test bits 12..31 of *both* args + be Lmul_shortway ! if zero, can do it the short way + andcc %g0, %g0, %o4 ! zero the partial product and clear N and V + + /* + * Long multiply. 32 steps, followed by a final shift step. + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %o1, %o4 ! 13 + mulscc %o4, %o1, %o4 ! 14 + mulscc %o4, %o1, %o4 ! 15 + mulscc %o4, %o1, %o4 ! 16 + mulscc %o4, %o1, %o4 ! 17 + mulscc %o4, %o1, %o4 ! 18 + mulscc %o4, %o1, %o4 ! 19 + mulscc %o4, %o1, %o4 ! 20 + mulscc %o4, %o1, %o4 ! 21 + mulscc %o4, %o1, %o4 ! 22 + mulscc %o4, %o1, %o4 ! 23 + mulscc %o4, %o1, %o4 ! 24 + mulscc %o4, %o1, %o4 ! 25 + mulscc %o4, %o1, %o4 ! 26 + mulscc %o4, %o1, %o4 ! 27 + mulscc %o4, %o1, %o4 ! 28 + mulscc %o4, %o1, %o4 ! 29 + mulscc %o4, %o1, %o4 ! 30 + mulscc %o4, %o1, %o4 ! 31 + mulscc %o4, %o1, %o4 ! 32 + mulscc %o4, %g0, %o4 ! final shift + + + /* + * Normally, with the shift-and-add approach, if both numbers are + * positive you get the correct result. WIth 32-bit two's-complement + * numbers, -x is represented as + * + * x 32 + * ( 2 - ------ ) mod 2 * 2 + * 32 + * 2 + * + * (the `mod 2' subtracts 1 from 1.bbbb). To avoid lots of 2^32s, + * we can treat this as if the radix point were just to the left + * of the sign bit (multiply by 2^32), and get + * + * -x = (2 - x) mod 2 + * + * Then, ignoring the `mod 2's for convenience: + * + * x * y = xy + * -x * y = 2y - xy + * x * -y = 2x - xy + * -x * -y = 4 - 2x - 2y + xy + * + * For signed multiplies, we subtract (x << 32) from the partial + * product to fix this problem for negative multipliers (see mul.s). + * Because of the way the shift into the partial product is calculated + * (N xor V), this term is automatically removed for the multiplicand, + * so we don't have to adjust. + * + * But for unsigned multiplies, the high order bit wasn't a sign bit, + * and the correction is wrong. So for unsigned multiplies where the + * high order bit is one, we end up with xy - (y << 32). To fix it + * we add y << 32. + */ + tst %o1 + bl,a 1f ! if %o1 < 0 (high order bit = 1), + add %o4, %o0, %o4 ! %o4 += %o0 (add y to upper half) +1: rd %y, %o0 ! get lower half of product + retl + addcc %o4, %g0, %o1 ! put upper half in place and set Z for %o1==0 + +Lmul_shortway: + /* + * Short multiply. 12 steps, followed by a final shift step. + * The resulting bits are off by 12 and (32-12) = 20 bit positions, + * but there is no problem with %o0 being negative (unlike above), + * and overflow is impossible (the answer is at most 24 bits long). + */ + mulscc %o4, %o1, %o4 ! 1 + mulscc %o4, %o1, %o4 ! 2 + mulscc %o4, %o1, %o4 ! 3 + mulscc %o4, %o1, %o4 ! 4 + mulscc %o4, %o1, %o4 ! 5 + mulscc %o4, %o1, %o4 ! 6 + mulscc %o4, %o1, %o4 ! 7 + mulscc %o4, %o1, %o4 ! 8 + mulscc %o4, %o1, %o4 ! 9 + mulscc %o4, %o1, %o4 ! 10 + mulscc %o4, %o1, %o4 ! 11 + mulscc %o4, %o1, %o4 ! 12 + mulscc %o4, %g0, %o4 ! final shift + + /* + * %o4 has 20 of the bits that should be in the result; %y has + * the bottom 12 (as %y's top 12). That is: + * + * %o4 %y + * +----------------+----------------+ + * | -12- | -20- | -12- | -20- | + * +------(---------+------)---------+ + * -----result----- + * + * The 12 bits of %o4 left of the `result' area are all zero; + * in fact, all top 20 bits of %o4 are zero. + */ + + rd %y, %o5 + sll %o4, 12, %o0 ! shift middle bits left 12 + srl %o5, 20, %o5 ! shift low bits right 20 + or %o5, %o0, %o0 + retl + addcc %g0, %g0, %o1 ! %o1 = zero, and set Z diff --git a/usr/klibc/arch/sparc64/Kbuild b/usr/klibc/arch/sparc64/Kbuild new file mode 100644 index 0000000..dbc6c65 --- /dev/null +++ b/usr/klibc/arch/sparc64/Kbuild @@ -0,0 +1,8 @@ +# +# klibc files for sparc64 +# + +klib-y := pipe.o setjmp.o sigaction.o syscall.o sysfork.o + +always := crt0.o +targets := crt0.o diff --git a/usr/klibc/arch/sparc64/MCONFIG b/usr/klibc/arch/sparc64/MCONFIG new file mode 100644 index 0000000..d8c878a --- /dev/null +++ b/usr/klibc/arch/sparc64/MCONFIG @@ -0,0 +1,24 @@ +# -*- makefile -*- +# +# arch/sparc64/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# + +KLIBCARCHREQFLAGS = -m64 -D__sparc64__ +KLIBCOPTFLAGS += -Os +KLIBCBITSIZE = 64 + +KLIBCLDFLAGS = -m elf64_sparc + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# Normal binaries start at 1 MB; the linker wants 1 MB alignment, +# and call instructions have a 30-bit signed offset, << 2. +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x80000000 + +# Kernel has never used stack trampolines +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/sparc64/crt0.S b/usr/klibc/arch/sparc64/crt0.S new file mode 100644 index 0000000..5faee7c --- /dev/null +++ b/usr/klibc/arch/sparc64/crt0.S @@ -0,0 +1,2 @@ +#define TARGET_PTR_SIZE 64 +#include "../sparc/crt0i.S" diff --git a/usr/klibc/arch/sparc64/pipe.S b/usr/klibc/arch/sparc64/pipe.S new file mode 100644 index 0000000..cb5c2c7 --- /dev/null +++ b/usr/klibc/arch/sparc64/pipe.S @@ -0,0 +1,31 @@ +/* + * arch/sparc64/pipe.S + * + * The pipe system call are special on sparc[64]: + * they return the two file descriptors in %o0 and %o1. + */ + +#include <machine/asm.h> +#include <asm/unistd.h> + + .globl pipe + .type pipe,#function + .align 4 +pipe: + mov __NR_pipe, %g1 + or %o0, 0, %g4 + t 0x6d + bcc %xcc, 1f + nop + PIC_PROLOGUE(%g1,%g4) + SET(errno,%g1,%g4) + st %o0,[%g4] + retl + mov -1, %o0 +1: + st %o0,[%g4] + st %o1,[%g4+4] + retl + mov 0, %o0 + + .size pipe,.-pipe diff --git a/usr/klibc/arch/sparc64/setjmp.S b/usr/klibc/arch/sparc64/setjmp.S new file mode 100644 index 0000000..75a6a68 --- /dev/null +++ b/usr/klibc/arch/sparc64/setjmp.S @@ -0,0 +1,55 @@ +! +! setjmp.S +! +! Basic setjmp/longjmp +! +! This code was based on the equivalent code in NetBSD +! + +! +! The jmp_buf contains the following entries: +! sp +! fp +! pc +! + .text + .align 4 + .global setjmp + .type setjmp, @function +setjmp: + stx %sp,[%o0+0] ! Callers stack pointer + stx %o7,[%o0+8] ! Return pc + stx %fp,[%o0+16] ! Frame pointer + retl ! Return + clr %o0 ! ...0 + + .size setjmp,.-setjmp + + + .globl longjmp + .type longjmp, @function +longjmp: + mov %o1, %g4 ! save return value + mov %o0, %g1 ! save target + ldx [%g1+16],%g5 ! get callers frame +1: + cmp %fp, %g5 ! compare against desired frame + bl,a 1b ! if below... + restore ! pop frame and loop + be,a 2f ! if there... + ldx [%g1+0],%o2 ! fetch return %sp + +.Lbotch: + unimp 0 ! ... error ... + +2: + cmp %o2, %sp ! %sp must not decrease + bl .Lbotch + nop + mov %o2, %sp ! it is OK, put it in place + + ldx [%g1+8],%o3 ! fetch %pc + jmp %o3 + 8 ! if sucess... + mov %g4,%o0 ! return %g4 + + .size longjmp,.-longjmp diff --git a/usr/klibc/arch/sparc64/sigaction.c b/usr/klibc/arch/sparc64/sigaction.c new file mode 100644 index 0000000..5c31a52 --- /dev/null +++ b/usr/klibc/arch/sparc64/sigaction.c @@ -0,0 +1,21 @@ +/* + * sigaction.c + */ + +#include <signal.h> +#include <sys/syscall.h> + +__extern void __sigreturn(void); +__extern int ____rt_sigaction(int, const struct sigaction *, struct sigaction *, + void (*)(void), size_t); + +int __rt_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact, size_t size) +{ + void (*restorer)(void); + + restorer = (act && act->sa_flags & SA_RESTORER) + ? (void (*)(void))((uintptr_t)act->sa_restorer - 8) + : NULL; + return ____rt_sigaction(sig, act, oact, restorer, size); +} diff --git a/usr/klibc/arch/sparc64/syscall.S b/usr/klibc/arch/sparc64/syscall.S new file mode 100644 index 0000000..c84c9ae --- /dev/null +++ b/usr/klibc/arch/sparc64/syscall.S @@ -0,0 +1,20 @@ +/* + * arch/sparc64/syscall.S + * + * Common system-call stub; %g1 already set to syscall number + */ + +#include <machine/asm.h> + + .globl __syscall_common + .type __syscall_common,#function + .align 4 +__syscall_common: + t 0x6d + bcc %xcc, 1f + PIC_PROLOGUE(%g1,%g4) + SET(errno,%g1,%g4) + st %o0,[%g4] +1: + retl + movcs %xcc, -1, %o0 diff --git a/usr/klibc/arch/sparc64/sysfork.S b/usr/klibc/arch/sparc64/sysfork.S new file mode 100644 index 0000000..a0c1334 --- /dev/null +++ b/usr/klibc/arch/sparc64/sysfork.S @@ -0,0 +1,28 @@ +/* + * arch/sparc64/sysfork.S + * + * The fork and vfork system calls are special on sparc[64]: + * they return the "other process" pid in %o0 and the + * "is child" flag in %o1 + * + * Common system-call stub; %g1 already set to syscall number + */ + +#include <machine/asm.h> + + .globl __syscall_forkish + .type __syscall_forkish,#function + .align 4 +__syscall_forkish: + t 0x6d + sub %o1, 1, %o1 + bcc,a %xcc, 1f + and %o0, %o1, %o0 + PIC_PROLOGUE(%g1,%g4) + SET(errno,%g1,%g4) + st %o0, [%g4] + retl + mov -1, %o0 +1: + retl + nop diff --git a/usr/klibc/arch/sparc64/sysstub.ph b/usr/klibc/arch/sparc64/sysstub.ph new file mode 100644 index 0000000..deeb88c --- /dev/null +++ b/usr/klibc/arch/sparc64/sysstub.ph @@ -0,0 +1,25 @@ +# -*- perl -*- +# +# arch/sparc64/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + $stype = $stype || 'common'; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.type ${fname},\@function\n"; + print OUT "\t.globl ${fname}\n"; + print OUT "${fname}:\n"; + print OUT "\tb __syscall_${stype}\n"; + print OUT "\t mov\t__NR_${sname}, %g1\n"; + print OUT "\t.size ${fname},.-${fname}\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/x86_64/Kbuild b/usr/klibc/arch/x86_64/Kbuild new file mode 100644 index 0000000..3f69c5b --- /dev/null +++ b/usr/klibc/arch/x86_64/Kbuild @@ -0,0 +1,8 @@ +# -*- makefile -*- +# +# klibc files for x86_64 + +always := crt0.o +targets := crt0.o + +klib-y := setjmp.o syscall.o sigreturn.o vfork.o diff --git a/usr/klibc/arch/x86_64/MCONFIG b/usr/klibc/arch/x86_64/MCONFIG new file mode 100644 index 0000000..70d690e --- /dev/null +++ b/usr/klibc/arch/x86_64/MCONFIG @@ -0,0 +1,43 @@ +# -*- makefile -*- +# +# arch/x86-64/MCONFIG +# +# Special rules for this architecture. Note that this is actually +# included from the main Makefile, and that pathnames should be +# accordingly. +# +# Blatantly copied and modified from i386 version by Mats Petersson, AMD. +# + +# +# NOTE: -fno-asynchronous-unwind-tables produce significantly smaller +# binaries (20% smaller), but makes the code completely useless for +# debugging using gdb. +# +KLIBCARCHREQFLAGS = -m64 +KLIBCOPTFLAGS += -Os -fomit-frame-pointer -mno-sse \ + $(call cc-option,-falign-functions=1, ) \ + $(call cc-option,-falign-jumps=1, ) \ + $(call cc-option,-falign-loops=1, ) +ifeq ($(DEBUG),y) +KLIBCOPTFLAGS += -g +else +KLIBCOPTFLAGS += -fno-asynchronous-unwind-tables +endif +KLIBCBITSIZE = 64 +KLIBCLDFLAGS = -m elf_x86_64 $(LD_IMAGE_BASE_OPT) 0x00400000 + +# Extra linkflags when building the shared version of the library +# This address needs to be reachable using normal inter-module +# calls, and work on the memory models for this architecture +# 2 MB - normal binaries start at 4 MB +# +# binutils now uses max-page-size=0x200000 by default, which pushes +# klibc.so data over the 4 MB mark, overlapping the executable. +# The old default was max-page-size=0x100000, but that also results +# in a broken layout with binutils 2.30. Since there's no +# architectural page size betwen 4 KB and 2MB, set it to 4 KB. +KLIBCSHAREDFLAGS = $(LD_IMAGE_BASE_OPT) 0x00200000 -z max-page-size=0x1000 + +# Kernel has never used stack trampolines +KLIBCEXECSTACK := n diff --git a/usr/klibc/arch/x86_64/crt0.S b/usr/klibc/arch/x86_64/crt0.S new file mode 100644 index 0000000..6a5f335 --- /dev/null +++ b/usr/klibc/arch/x86_64/crt0.S @@ -0,0 +1,22 @@ +# +# arch/x86_64/crt0.S +# +# Does arch-specific initialization and invokes __libc_init +# with the appropriate arguments. +# +# See __static_init.c or __shared_init.c for the expected +# arguments. +# + + .text + .align 4 + .type _start,@function + .globl _start +_start: + movq %rsp,%rdi # Offset of the ELF data structure + movq %rdx,%rsi # The atexit() pointer (if any) + call __libc_init + # We should never get here... + hlt + + .size _start,.-_start diff --git a/usr/klibc/arch/x86_64/setjmp.S b/usr/klibc/arch/x86_64/setjmp.S new file mode 100644 index 0000000..45f547b --- /dev/null +++ b/usr/klibc/arch/x86_64/setjmp.S @@ -0,0 +1,54 @@ +# +# arch/x86_64/setjmp.S +# +# setjmp/longjmp for the x86-64 architecture +# + +# +# The jmp_buf is assumed to contain the following, in order: +# %rbx +# %rsp (post-return) +# %rbp +# %r12 +# %r13 +# %r14 +# %r15 +# <return address> +# + + .text + .align 4 + .globl setjmp + .type setjmp, @function +setjmp: + pop %rsi # Return address, and adjust the stack + xorl %eax,%eax # Return value + movq %rbx,(%rdi) + movq %rsp,8(%rdi) # Post-return %rsp! + push %rsi # Make the call/return stack happy + movq %rbp,16(%rdi) + movq %r12,24(%rdi) + movq %r13,32(%rdi) + movq %r14,40(%rdi) + movq %r15,48(%rdi) + movq %rsi,56(%rdi) # Return address + ret + + .size setjmp,.-setjmp + + .text + .align 4 + .globl longjmp + .type longjmp, @function +longjmp: + movl %esi,%eax # Return value (int) + movq (%rdi),%rbx + movq 8(%rdi),%rsp + movq 16(%rdi),%rbp + movq 24(%rdi),%r12 + movq 32(%rdi),%r13 + movq 40(%rdi),%r14 + movq 48(%rdi),%r15 + jmp *56(%rdi) + + .size longjmp,.-longjmp diff --git a/usr/klibc/arch/x86_64/sigreturn.S b/usr/klibc/arch/x86_64/sigreturn.S new file mode 100644 index 0000000..46a5a0b --- /dev/null +++ b/usr/klibc/arch/x86_64/sigreturn.S @@ -0,0 +1,15 @@ +/* + * arch/x86_64/sigreturn.S + */ + +#include <asm/unistd.h> + + .text + .align 4 + .globl __sigreturn + .type __sigreturn,@function +__sigreturn: + movl $__NR_rt_sigreturn,%eax + syscall + + .size __sigreturn,.-__sigreturn diff --git a/usr/klibc/arch/x86_64/syscall.S b/usr/klibc/arch/x86_64/syscall.S new file mode 100644 index 0000000..1797797 --- /dev/null +++ b/usr/klibc/arch/x86_64/syscall.S @@ -0,0 +1,28 @@ +/* + * arch/x86-64/syscall.S + * + * Common tail-handling code for system calls. + * + * The arguments are in the standard argument registers; the system + * call number in %eax. + */ + .text + .align 4 + .globl __syscall_common + .type __syscall_common,@function +__syscall_common: + movq %rcx,%r10 # The kernel uses %r10 istf %rcx + syscall + + cmpq $-4095,%rax + jnb 1f + ret + + # Error return, must set errno +1: + negl %eax + movl %eax,errno(%rip) # errno is type int, so 32 bits + orq $-1,%rax # orq $-1 smaller than movq $-1 + ret + + .size __syscall_common,.-__syscall_common diff --git a/usr/klibc/arch/x86_64/sysstub.ph b/usr/klibc/arch/x86_64/sysstub.ph new file mode 100644 index 0000000..e2d797b --- /dev/null +++ b/usr/klibc/arch/x86_64/sysstub.ph @@ -0,0 +1,23 @@ +# -*- perl -*- +# +# arch/x86_64/sysstub.ph +# +# Script to generate system call stubs +# + +sub make_sysstub($$$$$@) { + my($outputdir, $fname, $type, $sname, $stype, @args) = @_; + + open(OUT, '>', "${outputdir}/${fname}.S"); + print OUT "#include <asm/unistd.h>\n"; + print OUT "\n"; + print OUT "\t.type ${fname},\@function\n"; + print OUT "\t.globl ${fname}\n"; + print OUT "${fname}:\n"; + print OUT "\tmovl \$__NR_${sname},%eax\n"; # Zero-extends to 64 bits + print OUT "\tjmp __syscall_common\n"; + print OUT "\t.size ${fname},.-${fname}\n"; + close(OUT); +} + +1; diff --git a/usr/klibc/arch/x86_64/vfork.S b/usr/klibc/arch/x86_64/vfork.S new file mode 100644 index 0000000..e1c8090 --- /dev/null +++ b/usr/klibc/arch/x86_64/vfork.S @@ -0,0 +1,26 @@ +# +# usr/klibc/arch/x86_64/vfork.S +# +# vfork is nasty - there must be nothing at all on the stack above +# the stack frame of the enclosing function. +# + +#include <asm/unistd.h> + + .text + .align 4 + .globl vfork + .type vfork, @function +vfork: + pop %rdx /* Return address */ + movl $__NR_vfork, %eax + syscall + push %rdx + cmpq $-4095, %rax + jae 1f + ret +1: + negl %eax + movl %eax, errno(%rip) + orq $-1, %rax + ret diff --git a/usr/klibc/asprintf.c b/usr/klibc/asprintf.c new file mode 100644 index 0000000..42f3aa2 --- /dev/null +++ b/usr/klibc/asprintf.c @@ -0,0 +1,19 @@ +/* + * asprintf.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +int asprintf(char **bufp, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vasprintf(bufp, format, ap); + va_end(ap); + + return rv; +} diff --git a/usr/klibc/assert.c b/usr/klibc/assert.c new file mode 100644 index 0000000..cca4129 --- /dev/null +++ b/usr/klibc/assert.c @@ -0,0 +1,14 @@ +/* + * assert.c + */ + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <klibc/compiler.h> + +__noreturn __assert_fail(const char *expr, const char *file, unsigned int line) +{ + printf("Assertion %s failed, file %s, line %u\n", expr, file, line); + abort(); +} diff --git a/usr/klibc/atexit.c b/usr/klibc/atexit.c new file mode 100644 index 0000000..af1b461 --- /dev/null +++ b/usr/klibc/atexit.c @@ -0,0 +1,10 @@ +/* + * atexit.c + */ + +#include <stdlib.h> + +int atexit(void (*fctn) (void)) +{ + return on_exit((void (*)(int, void *))fctn, NULL); +} diff --git a/usr/klibc/atexit.h b/usr/klibc/atexit.h new file mode 100644 index 0000000..5018689 --- /dev/null +++ b/usr/klibc/atexit.h @@ -0,0 +1,18 @@ +/* + * atexit.h + * + * atexit()/on_exit() internal definitions + */ + +#ifndef ATEXIT_H +#define ATEXIT_H + +struct atexit { + void (*fctn) (int, void *); + void *arg; /* on_exit() parameter */ + struct atexit *next; +}; + +extern struct atexit *__atexit_list; + +#endif /* ATEXIT_H */ diff --git a/usr/klibc/atoi.c b/usr/klibc/atoi.c new file mode 100644 index 0000000..a6ec0bf --- /dev/null +++ b/usr/klibc/atoi.c @@ -0,0 +1,3 @@ +#define TYPE int +#define NAME atoi +#include "atox.c" diff --git a/usr/klibc/atol.c b/usr/klibc/atol.c new file mode 100644 index 0000000..e65484e --- /dev/null +++ b/usr/klibc/atol.c @@ -0,0 +1,3 @@ +#define TYPE long +#define NAME atol +#include "atox.c" diff --git a/usr/klibc/atoll.c b/usr/klibc/atoll.c new file mode 100644 index 0000000..25df79e --- /dev/null +++ b/usr/klibc/atoll.c @@ -0,0 +1,3 @@ +#define TYPE long long +#define NAME atoll +#include "atox.c" diff --git a/usr/klibc/atox.c b/usr/klibc/atox.c new file mode 100644 index 0000000..c013bb4 --- /dev/null +++ b/usr/klibc/atox.c @@ -0,0 +1,14 @@ +/* + * atox.c + * + * atoi(), atol(), atoll() + */ + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> + +TYPE NAME(const char *nptr) +{ + return (TYPE) strntoumax(nptr, (char **)NULL, 10, ~(size_t) 0); +} diff --git a/usr/klibc/brk.c b/usr/klibc/brk.c new file mode 100644 index 0000000..df0bb7b --- /dev/null +++ b/usr/klibc/brk.c @@ -0,0 +1,29 @@ +/* brk.c - Change data segment size */ + +/* Written 2000 by Werner Almesberger */ + +#include <stddef.h> +#include <unistd.h> +#include <sys/types.h> +#include "malloc.h" + +#if !_KLIBC_NO_MMU /* uClinux doesn't have brk() */ + +char *__current_brk; + +/* + * The Linux brk() isn't what most people expect, so we call the + * system call __brk() and provide a wrapper. + */ +int brk(void *end_data_segment) +{ + char *new_brk; + + new_brk = __brk(end_data_segment); + if (new_brk != end_data_segment) + return -1; + __current_brk = new_brk; + return 0; +} + +#endif diff --git a/usr/klibc/bsd_signal.c b/usr/klibc/bsd_signal.c new file mode 100644 index 0000000..3d78d2c --- /dev/null +++ b/usr/klibc/bsd_signal.c @@ -0,0 +1,14 @@ +/* + * bsd_signal.c + */ + +#include <signal.h> + +__sighandler_t bsd_signal(int signum, __sighandler_t handler) +{ + /* BSD signal() semantics */ + return __signal(signum, handler, SA_RESTART); +} + +__sighandler_t signal(int signum, __sighandler_t handler) + __alias("bsd_signal"); diff --git a/usr/klibc/bsearch.c b/usr/klibc/bsearch.c new file mode 100644 index 0000000..1c8b07f --- /dev/null +++ b/usr/klibc/bsearch.c @@ -0,0 +1,26 @@ +/* + * bsearch.c + */ + +#include <stdlib.h> + +void *bsearch(const void *key, const void *base, size_t nmemb, + size_t size, int (*cmp) (const void *, const void *)) +{ + while (nmemb) { + size_t mididx = nmemb / 2; + const void *midobj = base + mididx * size; + int diff = cmp(key, midobj); + + if (diff == 0) + return (void *)midobj; + + if (diff > 0) { + base = midobj + size; + nmemb -= mididx + 1; + } else + nmemb = mididx; + } + + return NULL; +} diff --git a/usr/klibc/bzero.c b/usr/klibc/bzero.c new file mode 100644 index 0000000..aa1c1ff --- /dev/null +++ b/usr/klibc/bzero.c @@ -0,0 +1,6 @@ +#include <string.h> + +void bzero(void *dst, size_t n) +{ + memset(dst, 0, n); +} diff --git a/usr/klibc/calloc.c b/usr/klibc/calloc.c new file mode 100644 index 0000000..4a81cda --- /dev/null +++ b/usr/klibc/calloc.c @@ -0,0 +1,18 @@ +/* + * calloc.c + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +void *calloc(size_t nmemb, size_t size) +{ + unsigned long prod; + + if (__builtin_umull_overflow(nmemb, size, &prod)) { + errno = ENOMEM; + return NULL; + } + return zalloc(prod); +} diff --git a/usr/klibc/chmod.c b/usr/klibc/chmod.c new file mode 100644 index 0000000..b5129e6 --- /dev/null +++ b/usr/klibc/chmod.c @@ -0,0 +1,13 @@ +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syscall.h> + +#ifndef __NR_chmod + +int chmod(const char *path, mode_t mode) +{ + return fchmodat(AT_FDCWD, path, mode, 0); +} + +#endif /* __NR_chmod */ diff --git a/usr/klibc/chown.c b/usr/klibc/chown.c new file mode 100644 index 0000000..089cfc5 --- /dev/null +++ b/usr/klibc/chown.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_chown + +int chown(const char *path, uid_t owner, gid_t group) +{ + return fchownat(AT_FDCWD, path, owner, group, 0); +} + +#endif /* __NR_chown */ diff --git a/usr/klibc/clearenv.c b/usr/klibc/clearenv.c new file mode 100644 index 0000000..7c2435e --- /dev/null +++ b/usr/klibc/clearenv.c @@ -0,0 +1,17 @@ +/* + * clearenv.c + * + * Empty the environment + */ + +#include <stdlib.h> +#include <unistd.h> +#include "env.h" + +/* Note: if environ has been malloc'd, it will be freed on the next + setenv() or putenv() */ +int clearenv(void) +{ + environ = (char **)__null_environ; + return 0; +} diff --git a/usr/klibc/clock_nanosleep.c b/usr/klibc/clock_nanosleep.c new file mode 100644 index 0000000..b4fd1e2 --- /dev/null +++ b/usr/klibc/clock_nanosleep.c @@ -0,0 +1,17 @@ +#include <time.h> +#include <sys/time.h> +#include <sys/syscall.h> + +extern int __clock_nanosleep(clockid_t, int, + const struct timespec *, struct timespec *); + +/* + * POSIX says this has to return a positive error code, but the system + * call returns error codes in the usual way. + */ +int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, struct timespec *remain) +{ + return __clock_nanosleep(clock_id, flags, request, remain) ? + errno : 0; +} diff --git a/usr/klibc/closelog.c b/usr/klibc/closelog.c new file mode 100644 index 0000000..0437710 --- /dev/null +++ b/usr/klibc/closelog.c @@ -0,0 +1,18 @@ +/* + * closelog.c + */ + +#include <syslog.h> +#include <unistd.h> + +extern int __syslog_fd; + +void closelog(void) +{ + int logfd = __syslog_fd; + + if (logfd != -1) { + close(logfd); + __syslog_fd = -1; + } +} diff --git a/usr/klibc/creat.c b/usr/klibc/creat.c new file mode 100644 index 0000000..b503bca --- /dev/null +++ b/usr/klibc/creat.c @@ -0,0 +1,12 @@ +/* + * creat.c + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +int creat(const char *pathname, mode_t mode) +{ + return open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); +} diff --git a/usr/klibc/ctype/ctypefunc.h b/usr/klibc/ctype/ctypefunc.h new file mode 100644 index 0000000..f9e247d --- /dev/null +++ b/usr/klibc/ctype/ctypefunc.h @@ -0,0 +1,13 @@ +/* + * ctype/ctype.h + * + * Common header for out-of-line ctype functions + */ + +#define __CTYPE_NO_INLINE +#include <ctype.h> + +#define CTYPEFUNC(X) \ + int X(int c) { \ + return __ctype_##X(c); \ + } diff --git a/usr/klibc/ctype/isalnum.c b/usr/klibc/ctype/isalnum.c new file mode 100644 index 0000000..57af18b --- /dev/null +++ b/usr/klibc/ctype/isalnum.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isalnum) diff --git a/usr/klibc/ctype/isalpha.c b/usr/klibc/ctype/isalpha.c new file mode 100644 index 0000000..8f2effe --- /dev/null +++ b/usr/klibc/ctype/isalpha.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isalpha) diff --git a/usr/klibc/ctype/isascii.c b/usr/klibc/ctype/isascii.c new file mode 100644 index 0000000..6a974d5 --- /dev/null +++ b/usr/klibc/ctype/isascii.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isascii) diff --git a/usr/klibc/ctype/isblank.c b/usr/klibc/ctype/isblank.c new file mode 100644 index 0000000..7728550 --- /dev/null +++ b/usr/klibc/ctype/isblank.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isblank) diff --git a/usr/klibc/ctype/iscntrl.c b/usr/klibc/ctype/iscntrl.c new file mode 100644 index 0000000..81db804 --- /dev/null +++ b/usr/klibc/ctype/iscntrl.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(iscntrl) diff --git a/usr/klibc/ctype/isdigit.c b/usr/klibc/ctype/isdigit.c new file mode 100644 index 0000000..41a39d9 --- /dev/null +++ b/usr/klibc/ctype/isdigit.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isdigit) diff --git a/usr/klibc/ctype/isgraph.c b/usr/klibc/ctype/isgraph.c new file mode 100644 index 0000000..e5b83d1 --- /dev/null +++ b/usr/klibc/ctype/isgraph.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isgraph) diff --git a/usr/klibc/ctype/islower.c b/usr/klibc/ctype/islower.c new file mode 100644 index 0000000..9e179ae --- /dev/null +++ b/usr/klibc/ctype/islower.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(islower) diff --git a/usr/klibc/ctype/isprint.c b/usr/klibc/ctype/isprint.c new file mode 100644 index 0000000..9c6a351 --- /dev/null +++ b/usr/klibc/ctype/isprint.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isprint) diff --git a/usr/klibc/ctype/ispunct.c b/usr/klibc/ctype/ispunct.c new file mode 100644 index 0000000..36b5258 --- /dev/null +++ b/usr/klibc/ctype/ispunct.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(ispunct) diff --git a/usr/klibc/ctype/isspace.c b/usr/klibc/ctype/isspace.c new file mode 100644 index 0000000..aed06c8 --- /dev/null +++ b/usr/klibc/ctype/isspace.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isspace) diff --git a/usr/klibc/ctype/isupper.c b/usr/klibc/ctype/isupper.c new file mode 100644 index 0000000..70a139b --- /dev/null +++ b/usr/klibc/ctype/isupper.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isupper) diff --git a/usr/klibc/ctype/isxdigit.c b/usr/klibc/ctype/isxdigit.c new file mode 100644 index 0000000..ab89c1f --- /dev/null +++ b/usr/klibc/ctype/isxdigit.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(isxdigit) diff --git a/usr/klibc/ctype/tolower.c b/usr/klibc/ctype/tolower.c new file mode 100644 index 0000000..24ca72b --- /dev/null +++ b/usr/klibc/ctype/tolower.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(tolower) diff --git a/usr/klibc/ctype/toupper.c b/usr/klibc/ctype/toupper.c new file mode 100644 index 0000000..15763f7 --- /dev/null +++ b/usr/klibc/ctype/toupper.c @@ -0,0 +1,2 @@ +#include "ctypefunc.h" +CTYPEFUNC(toupper) diff --git a/usr/klibc/ctypes.c b/usr/klibc/ctypes.c new file mode 100644 index 0000000..deb566a --- /dev/null +++ b/usr/klibc/ctypes.c @@ -0,0 +1,284 @@ +/* + * ctypes.c + * + * This is the array that defines <ctype.h> classes. + * This assumes ISO 8859-1. + */ + +#include <ctype.h> + +const unsigned char __ctypes[257] = { + 0, /* EOF */ + + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl | __ctype_space, /* BS */ + __ctype_cntrl | __ctype_space, /* TAB */ + __ctype_cntrl | __ctype_space, /* LF */ + __ctype_cntrl | __ctype_space, /* VT */ + __ctype_cntrl | __ctype_space, /* FF */ + __ctype_cntrl | __ctype_space, /* CR */ + __ctype_cntrl, /* control character */ + + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + + __ctype_print | __ctype_space, /* space */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_digit | __ctype_xdigit, /* digit */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_upper | __ctype_xdigit, /* A-F */ + __ctype_print | __ctype_upper | __ctype_xdigit, /* A-F */ + __ctype_print | __ctype_upper | __ctype_xdigit, /* A-F */ + __ctype_print | __ctype_upper | __ctype_xdigit, /* A-F */ + __ctype_print | __ctype_upper | __ctype_xdigit, /* A-F */ + __ctype_print | __ctype_upper | __ctype_xdigit, /* A-F */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_upper, /* G-Z */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_lower | __ctype_xdigit, /* a-f */ + __ctype_print | __ctype_lower | __ctype_xdigit, /* a-f */ + __ctype_print | __ctype_lower | __ctype_xdigit, /* a-f */ + __ctype_print | __ctype_lower | __ctype_xdigit, /* a-f */ + __ctype_print | __ctype_lower | __ctype_xdigit, /* a-f */ + __ctype_print | __ctype_lower | __ctype_xdigit, /* a-f */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_lower, /* g-z */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_cntrl, /* control character */ + + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + + __ctype_print | __ctype_space, /* NBSP */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_punct, /* punctuation */ + + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_upper, /* upper accented */ + __ctype_print | __ctype_lower, /* lower accented */ + + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_punct, /* punctuation */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ + __ctype_print | __ctype_lower, /* lower accented */ +}; diff --git a/usr/klibc/daemon.c b/usr/klibc/daemon.c new file mode 100644 index 0000000..61b88dd --- /dev/null +++ b/usr/klibc/daemon.c @@ -0,0 +1,35 @@ +/* + * daemon.c - "daemonize" a process + */ + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +int daemon(int nochdir, int noclose) +{ + int nullfd; + pid_t f; + + if (!nochdir) { + if (chdir("/")) + return -1; + } + + if (!noclose) { + if ((nullfd = open("/dev/null", O_RDWR)) < 0 || + dup2(nullfd, 0) < 0 || + dup2(nullfd, 1) < 0 || + dup2(nullfd, 2) < 0) + return -1; + close(nullfd); + } + + f = fork(); + if (f < 0) + return -1; + else if (f > 0) + _exit(0); + + return setsid(); +} diff --git a/usr/klibc/dup2.c b/usr/klibc/dup2.c new file mode 100644 index 0000000..67e2171 --- /dev/null +++ b/usr/klibc/dup2.c @@ -0,0 +1,11 @@ +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_dup2 + +int dup2(int fd, int fd2) +{ + return dup3(fd, fd2, 0); +} + +#endif /* __NR_dup2 */ diff --git a/usr/klibc/endmntent.c b/usr/klibc/endmntent.c new file mode 100644 index 0000000..419c317 --- /dev/null +++ b/usr/klibc/endmntent.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include <mntent.h> + +int endmntent(FILE *fp) +{ + if (fp) + fclose(fp); + return 1; +} diff --git a/usr/klibc/env.h b/usr/klibc/env.h new file mode 100644 index 0000000..ae70be8 --- /dev/null +++ b/usr/klibc/env.h @@ -0,0 +1,10 @@ +#ifndef ENV_H +#define ENV_H + +/* str should be a duplicated version of the input string; + len is the length of the key including the = sign */ +int __put_env(char *str, size_t len, int overwrite); + +extern char *const __null_environ[]; + +#endif diff --git a/usr/klibc/exec_l.c b/usr/klibc/exec_l.c new file mode 100644 index 0000000..76b70df --- /dev/null +++ b/usr/klibc/exec_l.c @@ -0,0 +1,59 @@ +/* + * exec_l.c + * + * Common implementation of execl() execle() execlp() + */ + +#include <stdarg.h> +#include <alloca.h> +#include <unistd.h> + +int NAME(const char *path, const char *arg0, ...) +{ + va_list ap, cap; + int argc = 1, rv; + const char **argv, **argp; + const char *arg; + char *const *envp; + + va_start(ap, arg0); + va_copy(cap, ap); + + /* Count the number of arguments */ + do { + arg = va_arg(cap, const char *); + argc++; + } while (arg); + + va_end(cap); + + /* Allocate memory for the pointer array */ + argp = argv = alloca(argc * sizeof(const char *)); + if (!argv) { + va_end(ap); + return -1; + } + + /* Copy the list into an array */ + *argp++ = arg0; + do { + *argp++ = arg = va_arg(ap, const char *); + } while (arg); + +#if EXEC_E + /* execle() takes one more argument for the environment pointer */ + envp = va_arg(ap, char *const *); +#else + envp = environ; +#endif + +#if EXEC_P + rv = execvpe(path, (char * const *)argv, envp); +#else + rv = execve(path, (char * const *)argv, envp); +#endif + + va_end(ap); + + return rv; +} diff --git a/usr/klibc/execl.c b/usr/klibc/execl.c new file mode 100644 index 0000000..4581113 --- /dev/null +++ b/usr/klibc/execl.c @@ -0,0 +1,8 @@ +/* + * execl.c + */ + +#define NAME execl +#define EXEC_P 0 +#define EXEC_E 0 +#include "exec_l.c" diff --git a/usr/klibc/execle.c b/usr/klibc/execle.c new file mode 100644 index 0000000..b073988 --- /dev/null +++ b/usr/klibc/execle.c @@ -0,0 +1,8 @@ +/* + * execle.c + */ + +#define NAME execle +#define EXEC_P 0 +#define EXEC_E 1 +#include "exec_l.c" diff --git a/usr/klibc/execlp.c b/usr/klibc/execlp.c new file mode 100644 index 0000000..65c9aa4 --- /dev/null +++ b/usr/klibc/execlp.c @@ -0,0 +1,8 @@ +/* + * execlp.c + */ + +#define NAME execlp +#define EXEC_P 1 +#define EXEC_E 0 +#include "exec_l.c" diff --git a/usr/klibc/execlpe.c b/usr/klibc/execlpe.c new file mode 100644 index 0000000..fef972f --- /dev/null +++ b/usr/klibc/execlpe.c @@ -0,0 +1,8 @@ +/* + * execlpe.c + */ + +#define NAME execlpe +#define EXEC_P 1 +#define EXEC_E 1 +#include "exec_l.c" diff --git a/usr/klibc/execv.c b/usr/klibc/execv.c new file mode 100644 index 0000000..6894e79 --- /dev/null +++ b/usr/klibc/execv.c @@ -0,0 +1,10 @@ +/* + * execv.c + */ + +#include <unistd.h> + +int execv(const char *path, char *const *argv) +{ + return execve(path, argv, environ); +} diff --git a/usr/klibc/execvp.c b/usr/klibc/execvp.c new file mode 100644 index 0000000..7d8e2bc --- /dev/null +++ b/usr/klibc/execvp.c @@ -0,0 +1,10 @@ +/* + * execvp.c + */ + +#include <unistd.h> + +int execvp(const char *path, char *const *argv) +{ + return execvpe(path, argv, environ); +} diff --git a/usr/klibc/execvpe.c b/usr/klibc/execvpe.c new file mode 100644 index 0000000..0b844f2 --- /dev/null +++ b/usr/klibc/execvpe.c @@ -0,0 +1,75 @@ +/* + * execvpe.c + * + * execvpe() function (from which we build execlp, execlpe, execvp). + * + * This version of execvpe() will *not* spawn /bin/sh if the command + * return ENOEXEC. That's what #! is for, folks! + * + * Since execlpe() and execvpe() aren't in POSIX, nor in glibc, + * I have followed QNX precedent in the implementation of the PATH: + * the PATH that is used is the one in the current environment, not + * in the new environment. Otherwise it would be impossible to pass + * a different PATH to the new process than the one one would want to + * use to search. + */ + +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#define DEFAULT_PATH "/bin:/usr/bin:." + +int execvpe(const char *file, char *const *argv, char *const *envp) +{ + char path[PATH_MAX]; + const char *searchpath, *esp; + size_t prefixlen, filelen, totallen; + + if (strchr(file, '/')) /* Specific path */ + return execve(file, argv, envp); + + filelen = strlen(file); + + searchpath = getenv("PATH"); + if (!searchpath) + searchpath = DEFAULT_PATH; + + errno = ENOENT; /* Default errno, if execve() doesn't + change it */ + + do { + esp = strchr(searchpath, ':'); + if (esp) + prefixlen = esp - searchpath; + else + prefixlen = strlen(searchpath); + + if (prefixlen == 0 || searchpath[prefixlen - 1] == '/') { + totallen = prefixlen + filelen; + if (totallen >= PATH_MAX) + continue; + memcpy(path, searchpath, prefixlen); + memcpy(path + prefixlen, file, filelen); + } else { + totallen = prefixlen + filelen + 1; + if (totallen >= PATH_MAX) + continue; + memcpy(path, searchpath, prefixlen); + path[prefixlen] = '/'; + memcpy(path + prefixlen + 1, file, filelen); + } + path[totallen] = '\0'; + + execve(path, argv, envp); + if (errno == E2BIG || errno == ENOEXEC || + errno == ENOMEM || errno == ETXTBSY) + break; /* Report this as an error, no more search */ + + searchpath = esp + 1; + } while (esp); + + return -1; +} diff --git a/usr/klibc/exit.c b/usr/klibc/exit.c new file mode 100644 index 0000000..2368b07 --- /dev/null +++ b/usr/klibc/exit.c @@ -0,0 +1,32 @@ +/* + * exit.c + * + * exit(), including the handling of the atexit chain. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/syscall.h> +#include "atexit.h" + +/* Link chain for atexit/on_exit */ +struct atexit *__atexit_list; + +__noreturn exit(int rv) +{ + struct atexit *ap; + + for (ap = __atexit_list; ap; ap = ap->next) { + /* This assumes extra args are harmless. They should + be in all normal C ABIs, but if an architecture has + some particularly bizarre ABI this might be worth + watching out for. */ + ap->fctn(rv, ap->arg); + } + + /* Handle any library destructors if we ever start using them... */ + fflush(NULL); + + _exit(rv); +} diff --git a/usr/klibc/fgets.c b/usr/klibc/fgets.c new file mode 100644 index 0000000..b6e6f3e --- /dev/null +++ b/usr/klibc/fgets.c @@ -0,0 +1,28 @@ +/* + * fgets.c + */ + +#include <stdio.h> + +char *fgets(char *s, int n, FILE *f) +{ + int ch; + char *p = s; + + while (n > 1) { + ch = getc(f); + if (ch == EOF) { + s = NULL; + break; + } + *p++ = ch; + n--; + if (ch == '\n') + break; + } + if (n) + *p = '\0'; + + return s; +} +__ALIAS(char *, fgets_unlocked, (char *, int, FILE *), fgets) diff --git a/usr/klibc/fnmatch.c b/usr/klibc/fnmatch.c new file mode 100644 index 0000000..5d0a25f --- /dev/null +++ b/usr/klibc/fnmatch.c @@ -0,0 +1,72 @@ +/* + * fnmatch.c + * + * Original implementation by Kay Sievers, modified by H. Peter Anvin. + */ + +#include <fnmatch.h> + +int fnmatch(const char *p, const char *s, int flags) +{ + if (flags & FNM_PATHNAME && *s == '/') + return (*p != '/') || fnmatch(p+1, s+1, flags); + if (flags & FNM_PERIOD && *s == '.') + return (*p != '.') || fnmatch(p+1, s+1, flags); + + flags &= ~FNM_PERIOD; /* Only applies at beginning */ + + if (!(flags & FNM_NOESCAPE) && *p == '\\') { + p++; + return (*p != *s) || fnmatch(p+1, s+1, flags); + } + + if (*s == '\0') { + while (*p == '*') + p++; + return (*p != '\0'); + } + + switch (*p) { + case '[': + { + int not = 0; + p++; + if (*p == '!') { + not = 1; + p++; + } + while ((*p != '\0') && (*p != ']')) { + int match = 0; + if (p[1] == '-') { + if ((*s >= *p) && (*s <= p[2])) + match = 1; + p += 3; + } else { + match = (*p == *s); + p++; + } + if (match ^ not) { + while ((*p != '\0') && (*p != ']')) + p++; + if (*p == ']') + return fnmatch(p+1, s+1, flags); + } + } + } + break; + case '*': + if (fnmatch(p, s+1, flags)) + return fnmatch(p+1, s, flags); + return 0; + case '\0': + if (*s == '\0') { + return 0; + } + break; + default: + if ((*p == *s) || (*p == '?')) + return fnmatch(p+1, s+1, flags); + break; + } + return 1; +} diff --git a/usr/klibc/fork.c b/usr/klibc/fork.c new file mode 100644 index 0000000..b07486e --- /dev/null +++ b/usr/klibc/fork.c @@ -0,0 +1,21 @@ +/* + * fork.c + * + * This is normally just a syscall stub, but at least one system + * doesn't have sys_fork, only sys_clone... + */ + +#include <sys/syscall.h> +#include <signal.h> +#include <unistd.h> +#include <sched.h> +#include <klibc/sysconfig.h> + +#if !_KLIBC_NO_MMU && !defined(__NR_fork) + +pid_t fork(void) +{ + return __clone(SIGCHLD, 0); +} + +#endif /* __NR_fork */ diff --git a/usr/klibc/fprintf.c b/usr/klibc/fprintf.c new file mode 100644 index 0000000..8403923 --- /dev/null +++ b/usr/klibc/fprintf.c @@ -0,0 +1,19 @@ +/* + * fprintf.c + */ + +#include <stdio.h> +#include <stdarg.h> + +#define BUFFER_SIZE 16384 + +int fprintf(FILE * file, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vfprintf(file, format, ap); + va_end(ap); + return rv; +} diff --git a/usr/klibc/fputc.c b/usr/klibc/fputc.c new file mode 100644 index 0000000..7385d6f --- /dev/null +++ b/usr/klibc/fputc.c @@ -0,0 +1,15 @@ +/* + * fputc.c + * + * gcc "printf decompilation" expects this to exist... + */ + +#include <stdio.h> + +int fputc(int c, FILE *f) +{ + unsigned char ch = c; + + return _fwrite(&ch, 1, f) == 1 ? ch : EOF; +} +__ALIAS(int, fputc_unlocked, (int, FILE *), fputc) diff --git a/usr/klibc/fputs.c b/usr/klibc/fputs.c new file mode 100644 index 0000000..cbc2056 --- /dev/null +++ b/usr/klibc/fputs.c @@ -0,0 +1,16 @@ +/* + * fputs.c + * + * This isn't quite fputs() in the stdio sense, since we don't + * have stdio, but it takes a file descriptor argument instead + * of the FILE *. + */ + +#include <stdio.h> +#include <string.h> + +int fputs(const char *s, FILE *file) +{ + return _fwrite(s, strlen(s), file); +} +__ALIAS(int, fputs_unlocked, (const char *, FILE *), fputs) diff --git a/usr/klibc/fread2.c b/usr/klibc/fread2.c new file mode 100644 index 0000000..f5b2acb --- /dev/null +++ b/usr/klibc/fread2.c @@ -0,0 +1,14 @@ +/* + * fread2.c + * + * The actual fread() function as a non-inline + */ + +#define __NO_STDIO_INLINES +#include <stdio.h> + +size_t fread(void *ptr, size_t size, size_t nmemb, FILE * f) +{ + return _fread(ptr, size * nmemb, f) / size; +} +__ALIAS(size_t, fread_unlocked, (void *, size_t, size_t, FILE *), fread) diff --git a/usr/klibc/fstat.c b/usr/klibc/fstat.c new file mode 100644 index 0000000..36dd661 --- /dev/null +++ b/usr/klibc/fstat.c @@ -0,0 +1,10 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syscall.h> + +int fstat(int fd, struct stat *buf) +{ + return fstatat(fd, "", buf, AT_EMPTY_PATH); +} diff --git a/usr/klibc/fstatat.c b/usr/klibc/fstatat.c new file mode 100644 index 0000000..12b5093 --- /dev/null +++ b/usr/klibc/fstatat.c @@ -0,0 +1,36 @@ +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syscall.h> + +static void timespec_from_statx(struct timespec *ts, + const struct statx_timestamp *xts) +{ + ts->tv_sec = xts->tv_sec; + ts->tv_nsec = xts->tv_nsec; +} + +int fstatat(int dirfd, const char *path, struct stat *buf, int flags) +{ + struct statx xbuf; + + if (statx(dirfd, path, flags | AT_NO_AUTOMOUNT, STATX_BASIC_STATS, + &xbuf)) + return -1; + + buf->st_dev = makedev(xbuf.stx_dev_major, xbuf.stx_dev_minor); + buf->st_ino = xbuf.stx_ino; + buf->st_nlink = xbuf.stx_nlink; + buf->st_mode = xbuf.stx_mode; + buf->st_uid = xbuf.stx_uid; + buf->st_gid = xbuf.stx_gid; + buf->st_rdev = makedev(xbuf.stx_rdev_major, xbuf.stx_rdev_minor); + buf->st_size = xbuf.stx_size; + buf->st_blksize = xbuf.stx_blksize; + buf->st_blocks = xbuf.stx_blocks; + timespec_from_statx(&buf->st_atim, &xbuf.stx_atime); + timespec_from_statx(&buf->st_ctim, &xbuf.stx_ctime); + timespec_from_statx(&buf->st_mtim, &xbuf.stx_mtime); + return 0; +} diff --git a/usr/klibc/fstatfs.c b/usr/klibc/fstatfs.c new file mode 100644 index 0000000..614f2ec --- /dev/null +++ b/usr/klibc/fstatfs.c @@ -0,0 +1,19 @@ +/* + * fstatfs.c + * + * On architectures which do fstatfs64, wrap the system call + */ + +#include <sys/syscall.h> +#include <sys/vfs.h> + +#ifdef __NR_fstatfs64 + +extern int __fstatfs64(int, size_t, struct statfs *); + +int fstatfs(int fd, struct statfs *buf) +{ + return __fstatfs64(fd, sizeof *buf, buf); +} + +#endif diff --git a/usr/klibc/futimesat.c b/usr/klibc/futimesat.c new file mode 100644 index 0000000..f4da4ba --- /dev/null +++ b/usr/klibc/futimesat.c @@ -0,0 +1,18 @@ +#include <fcntl.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/syscall.h> + +int futimesat(int dirfd, const char *filename, const struct timeval tvp[2]) +{ + struct timespec ts[2]; + + if (tvp) { + ts[0].tv_sec = tvp[0].tv_sec; + ts[0].tv_nsec = tvp[0].tv_usec * 1000; + ts[1].tv_sec = tvp[1].tv_sec; + ts[1].tv_nsec = tvp[1].tv_usec * 1000; + } + + return utimensat(dirfd, filename, &ts[0], 0); +} diff --git a/usr/klibc/fwrite2.c b/usr/klibc/fwrite2.c new file mode 100644 index 0000000..a8c14c9 --- /dev/null +++ b/usr/klibc/fwrite2.c @@ -0,0 +1,15 @@ +/* + * fwrite2.c + * + * The actual fwrite() function as a non-inline + */ + +#define __NO_STDIO_INLINES +#include <stdio.h> + +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE * f) +{ + return _fwrite(ptr, size * nmemb, f) / size; +} +__ALIAS(size_t, fwrite_unlocked, (const void *, size_t, size_t, FILE *), + fwrite) diff --git a/usr/klibc/getcwd.c b/usr/klibc/getcwd.c new file mode 100644 index 0000000..22ec812 --- /dev/null +++ b/usr/klibc/getcwd.c @@ -0,0 +1,15 @@ +/* + * getcwd.c + * + * The system call behaves differently than the library function. + */ + +#include <unistd.h> +#include <sys/syscall.h> + +extern int __getcwd(char *buf, size_t size); + +char *getcwd(char *buf, size_t size) +{ + return (__getcwd(buf, size) < 0) ? NULL : buf; +} diff --git a/usr/klibc/getdomainname.c b/usr/klibc/getdomainname.c new file mode 100644 index 0000000..218ff0b --- /dev/null +++ b/usr/klibc/getdomainname.c @@ -0,0 +1,25 @@ +/* + * getdomainname.c + */ + +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <sys/utsname.h> + +int getdomainname(char *name, size_t len) +{ + struct utsname un; + + if (uname(&un)) + return -1; + + if (len < strlen(un.domainname) + 1) { + errno = EINVAL; + return -1; + } + + strcpy(name, un.domainname); + + return 0; +} diff --git a/usr/klibc/getenv.c b/usr/klibc/getenv.c new file mode 100644 index 0000000..3a4ae5e --- /dev/null +++ b/usr/klibc/getenv.c @@ -0,0 +1,24 @@ +/* + * getenv.c + */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +char *getenv(const char *name) +{ + char **p, *q; + int len = strlen(name); + + if (!environ) + return NULL; + + for (p = environ; (q = *p); p++) { + if (!strncmp(name, q, len) && q[len] == '=') { + return q + (len + 1); + } + } + + return NULL; +} diff --git a/usr/klibc/gethostname.c b/usr/klibc/gethostname.c new file mode 100644 index 0000000..120edd9 --- /dev/null +++ b/usr/klibc/gethostname.c @@ -0,0 +1,25 @@ +/* + * gethostname.c + */ + +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <sys/utsname.h> + +int gethostname(char *name, size_t len) +{ + struct utsname un; + + if (uname(&un)) + return -1; + + if (len < strlen(un.nodename) + 1) { + errno = EINVAL; + return -1; + } + + strcpy(name, un.nodename); + + return 0; +} diff --git a/usr/klibc/getmntent.c b/usr/klibc/getmntent.c new file mode 100644 index 0000000..8af27f3 --- /dev/null +++ b/usr/klibc/getmntent.c @@ -0,0 +1,61 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <mntent.h> + +#define BUFLEN 1024 + +struct mntent *getmntent_r(FILE *fp, struct mntent *mntbuf, char *buf, + int buflen) +{ + char *line = NULL, *saveptr = NULL; + const char *sep = " \t\n"; + + if (!fp || !mntbuf || !buf) + return NULL; + + while ((line = fgets(buf, buflen, fp)) != NULL) { + if (buf[0] == '#' || buf[0] == '\n') + continue; + break; + } + + if (!line) + return NULL; + + mntbuf->mnt_fsname = strtok_r(buf, sep, &saveptr); + if (!mntbuf->mnt_fsname) + return NULL; + + mntbuf->mnt_dir = strtok_r(NULL, sep, &saveptr); + if (!mntbuf->mnt_fsname) + return NULL; + + mntbuf->mnt_type = strtok_r(NULL, sep, &saveptr); + if (!mntbuf->mnt_type) + return NULL; + + mntbuf->mnt_opts = strtok_r(NULL, sep, &saveptr); + if (!mntbuf->mnt_opts) + mntbuf->mnt_opts = ""; + + line = strtok_r(NULL, sep, &saveptr); + mntbuf->mnt_freq = !line ? 0 : atoi(line); + + line = strtok_r(NULL, sep, &saveptr); + mntbuf->mnt_passno = !line ? 0 : atoi(line); + + return mntbuf; +} + +struct mntent *getmntent(FILE *fp) +{ + static char *buf = NULL; + static struct mntent mntbuf; + + buf = malloc(BUFLEN); + if (!buf) + perror("malloc"); + + return getmntent_r(fp, &mntbuf, buf, BUFLEN); +} diff --git a/usr/klibc/getopt.c b/usr/klibc/getopt.c new file mode 100644 index 0000000..806735d --- /dev/null +++ b/usr/klibc/getopt.c @@ -0,0 +1,97 @@ +/* + * getopt.c + * + * Simple POSIX getopt(), no GNU extensions... + */ + +#include <stdint.h> +#include <unistd.h> +#include <string.h> + +char *optarg; +int optind, opterr, optopt; +static struct getopt_private_state { + const char *optptr; + const char *last_optstring; + char *const *last_argv; +} pvt; + +int getopt(int argc, char *const *argv, const char *optstring) +{ + const char *carg; + const char *osptr; + int opt; + + /* getopt() relies on a number of different global state + variables, which can make this really confusing if there is + more than one use of getopt() in the same program. This + attempts to detect that situation by detecting if the + "optstring" or "argv" argument have changed since last time + we were called; if so, reinitialize the query state. */ + + if (optstring != pvt.last_optstring || argv != pvt.last_argv || + optind < 1 || optind > argc) { + /* optind doesn't match the current query */ + pvt.last_optstring = optstring; + pvt.last_argv = argv; + optind = 1; + pvt.optptr = NULL; + } + + carg = argv[optind]; + + /* First, eliminate all non-option cases */ + + if (!carg || carg[0] != '-' || !carg[1]) { + return -1; + } + + if (carg[1] == '-' && !carg[2]) { + optind++; + return -1; + } + + if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) { + /* Someone frobbed optind, change to new opt. */ + pvt.optptr = carg + 1; + } + + opt = *pvt.optptr++; + + if (opt != ':' && (osptr = strchr(optstring, opt))) { + if (osptr[1] == ':') { + if (*pvt.optptr) { + /* Argument-taking option with attached + argument */ + optarg = (char *)pvt.optptr; + optind++; + } else { + /* Argument-taking option with non-attached + argument */ + if (argv[optind + 1]) { + optarg = (char *)argv[optind+1]; + optind += 2; + } else { + /* Missing argument */ + optind++; + return (optstring[0] == ':') + ? ':' : '?'; + } + } + return opt; + } else { + /* Non-argument-taking option */ + /* pvt.optptr will remember the exact position to + resume at */ + if (!*pvt.optptr) + optind++; + return opt; + } + } else { + /* Unknown option */ + optopt = opt; + if (!*pvt.optptr) + optind++; + return '?'; + } +} diff --git a/usr/klibc/getopt_long.c b/usr/klibc/getopt_long.c new file mode 100644 index 0000000..e3d064b --- /dev/null +++ b/usr/klibc/getopt_long.c @@ -0,0 +1,152 @@ +/* + * getopt.c + * + * getopt_long(), or at least a common subset thereof: + * + * - Option reordering is not supported + * - -W foo is not supported + * - First optstring character "-" not supported. + */ + +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> + +char *optarg; +int optind, opterr, optopt; +static struct getopt_private_state { + const char *optptr; + const char *last_optstring; + char *const *last_argv; +} pvt; + +static inline const char *option_matches(const char *arg_str, + const char *opt_name) +{ + while (*arg_str != '\0' && *arg_str != '=') { + if (*arg_str++ != *opt_name++) + return NULL; + } + + if (*opt_name) + return NULL; + + return arg_str; +} + +int getopt_long(int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longindex) +{ + const char *carg; + const char *osptr; + int opt; + + /* getopt() relies on a number of different global state + variables, which can make this really confusing if there is + more than one use of getopt() in the same program. This + attempts to detect that situation by detecting if the + "optstring" or "argv" argument have changed since last time + we were called; if so, reinitialize the query state. */ + + if (optstring != pvt.last_optstring || argv != pvt.last_argv || + optind < 1 || optind > argc) { + /* optind doesn't match the current query */ + pvt.last_optstring = optstring; + pvt.last_argv = argv; + optind = 1; + pvt.optptr = NULL; + } + + carg = argv[optind]; + + /* First, eliminate all non-option cases */ + + if (!carg || carg[0] != '-' || !carg[1]) + return -1; + + if (carg[1] == '-') { + const struct option *lo; + const char *opt_end = NULL; + + optind++; + + /* Either it's a long option, or it's -- */ + if (!carg[2]) { + /* It's -- */ + return -1; + } + + for (lo = longopts; lo->name; lo++) { + if ((opt_end = option_matches(carg+2, lo->name))) + break; + } + if (!opt_end) + return '?'; + + if (longindex) + *longindex = lo-longopts; + + if (*opt_end == '=') { + if (lo->has_arg) + optarg = (char *)opt_end+1; + else + return '?'; + } else if (lo->has_arg == 1) { + if (!(optarg = argv[optind])) + return '?'; + optind++; + } + + if (lo->flag) { + *lo->flag = lo->val; + return 0; + } else { + return lo->val; + } + } + + if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) { + /* Someone frobbed optind, change to new opt. */ + pvt.optptr = carg + 1; + } + + opt = *pvt.optptr++; + + if (opt != ':' && (osptr = strchr(optstring, opt))) { + if (osptr[1] == ':') { + if (*pvt.optptr) { + /* Argument-taking option with attached + argument */ + optarg = (char *)pvt.optptr; + optind++; + } else { + /* Argument-taking option with non-attached + argument */ + if (argv[optind + 1]) { + optarg = (char *)argv[optind+1]; + optind += 2; + } else { + /* Missing argument */ + optind++; + return (optstring[0] == ':') + ? ':' : '?'; + } + } + return opt; + } else { + /* Non-argument-taking option */ + /* pvt.optptr will remember the exact position to + resume at */ + if (!*pvt.optptr) + optind++; + return opt; + } + } else { + /* Unknown option */ + optopt = opt; + if (!*pvt.optptr) + optind++; + return '?'; + } +} diff --git a/usr/klibc/getpgrp.c b/usr/klibc/getpgrp.c new file mode 100644 index 0000000..b20b17a --- /dev/null +++ b/usr/klibc/getpgrp.c @@ -0,0 +1,10 @@ +/* + * getpgrp.c + */ + +#include <unistd.h> + +pid_t getpgrp(void) +{ + return getpgid(0); +} diff --git a/usr/klibc/getpriority.c b/usr/klibc/getpriority.c new file mode 100644 index 0000000..01d6e06 --- /dev/null +++ b/usr/klibc/getpriority.c @@ -0,0 +1,23 @@ +/* + * getpriority.c + * + * Needs to do some post-syscall mangling to distinguish error returns... + * but only on some platforms. Sigh. + */ + +#include <unistd.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/syscall.h> + +#if !defined(__alpha__) && !defined(__ia64__) + +extern int __getpriority(int, int); + +int getpriority(int which, int who) +{ + int rv = __getpriority(which, who); + return (rv < 0) ? rv : 20-rv; +} + +#endif diff --git a/usr/klibc/gettimeofday.c b/usr/klibc/gettimeofday.c new file mode 100644 index 0000000..919c46d --- /dev/null +++ b/usr/klibc/gettimeofday.c @@ -0,0 +1,22 @@ +#include <time.h> +#include <sys/time.h> +#include <sys/syscall.h> + +extern int __gettimeofday(void *, struct timezone *); + +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + struct timespec ts; + + if (tv) { + if (clock_gettime(CLOCK_REALTIME, &ts)) + return -1; + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + } + + if (tz && __gettimeofday(NULL, tz)) + return -1; + + return 0; +} diff --git a/usr/klibc/globals.c b/usr/klibc/globals.c new file mode 100644 index 0000000..72ae91f --- /dev/null +++ b/usr/klibc/globals.c @@ -0,0 +1,10 @@ +/* + * globals.c + * + * These have to be defined somewhere... + */ +#include <errno.h> +#include <unistd.h> + +int errno; +char **environ; diff --git a/usr/klibc/inet/bindresvport.c b/usr/klibc/inet/bindresvport.c new file mode 100644 index 0000000..e22c1c2 --- /dev/null +++ b/usr/klibc/inet/bindresvport.c @@ -0,0 +1,46 @@ +/* + * inet/bindresvport.c + */ + +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <unistd.h> + +#define START_PORT 768 +#define END_PORT IPPORT_RESERVED +#define NUM_PORTS (END_PORT - START_PORT) + +int bindresvport(int sd, struct sockaddr_in *sin) +{ + struct sockaddr_in me; + static short port; + int ret = 0; + int i; + + if (sin == NULL) { + memset(&me, 0, sizeof(me)); + sin = &me; + sin->sin_family = AF_INET; + } else if (sin->sin_family != AF_INET) { + errno = EPFNOSUPPORT; + return -1; + } + + if (port == 0) + port = START_PORT + (getpid() % NUM_PORTS); + + for (i = 0; i < NUM_PORTS; i++, port++) { + if (port == END_PORT) + port = START_PORT; + sin->sin_port = htons(port); + + ret = bind(sd, (struct sockaddr *)sin, sizeof(*sin)); + if (ret != -1) + break; + } + + return ret; +} diff --git a/usr/klibc/inet/inet_addr.c b/usr/klibc/inet/inet_addr.c new file mode 100644 index 0000000..ba086c3 --- /dev/null +++ b/usr/klibc/inet/inet_addr.c @@ -0,0 +1,14 @@ +/* + * inet/inet_addr.c + */ + +#include <arpa/inet.h> +#include <stdio.h> + +uint32_t inet_addr(const char *str) +{ + struct in_addr a; + int rv = inet_aton(str, &a); + + return rv ? a.s_addr : INADDR_NONE; +} diff --git a/usr/klibc/inet/inet_aton.c b/usr/klibc/inet/inet_aton.c new file mode 100644 index 0000000..beceeea --- /dev/null +++ b/usr/klibc/inet/inet_aton.c @@ -0,0 +1,22 @@ +/* + * inet/inet_aton.c + */ + +#include <arpa/inet.h> +#include <stdio.h> + +int inet_aton(const char *str, struct in_addr *addr) +{ + union { + uint8_t b[4]; + uint32_t l; + } a; + + if (sscanf(str, "%hhu.%hhu.%hhu.%hhu", + &a.b[0], &a.b[1], &a.b[2], &a.b[3]) == 4) { + addr->s_addr = a.l; /* Always in network byte order */ + return 1; + } else { + return 0; + } +} diff --git a/usr/klibc/inet/inet_ntoa.c b/usr/klibc/inet/inet_ntoa.c new file mode 100644 index 0000000..6dbf057 --- /dev/null +++ b/usr/klibc/inet/inet_ntoa.c @@ -0,0 +1,16 @@ +/* + * inet/inet_ntoa.c + */ + +#include <stdint.h> +#include <arpa/inet.h> +#include <stdio.h> + +char *inet_ntoa(struct in_addr addr) +{ + static char name[16]; + const uint8_t *cp = (const uint8_t *) &addr.s_addr; + + sprintf(name, "%u.%u.%u.%u", cp[0], cp[1], cp[2], cp[3]); + return name; +} diff --git a/usr/klibc/inet/inet_ntop.c b/usr/klibc/inet/inet_ntop.c new file mode 100644 index 0000000..106fb45 --- /dev/null +++ b/usr/klibc/inet/inet_ntop.c @@ -0,0 +1,54 @@ +/* + * inet/inet_ntop.c + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in6.h> + +const char *inet_ntop(int af, const void *cp, char *buf, size_t len) +{ + size_t xlen; + + switch (af) { + case AF_INET: + { + const uint8_t *bp = (const uint8_t *) + &((const struct in_addr *)cp)->s_addr; + + xlen = snprintf(buf, len, "%u.%u.%u.%u", + bp[0], bp[1], bp[2], bp[3]); + } + break; + + case AF_INET6: + { + const struct in6_addr *s = (const struct in6_addr *)cp; + + xlen = snprintf(buf, len, "%x:%x:%x:%x:%x:%x:%x:%x", + ntohs(s->s6_addr16[0]), + ntohs(s->s6_addr16[1]), + ntohs(s->s6_addr16[2]), + ntohs(s->s6_addr16[3]), + ntohs(s->s6_addr16[4]), + ntohs(s->s6_addr16[5]), + ntohs(s->s6_addr16[6]), + ntohs(s->s6_addr16[7])); + } + break; + + default: + errno = EAFNOSUPPORT; + return NULL; + } + + if (xlen > len) { + errno = ENOSPC; + return NULL; + } + + return buf; +} diff --git a/usr/klibc/inet/inet_pton.c b/usr/klibc/inet/inet_pton.c new file mode 100644 index 0000000..19fe16e --- /dev/null +++ b/usr/klibc/inet/inet_pton.c @@ -0,0 +1,78 @@ +/* + * inet/inet_pton.c + */ + +#include <stdio.h> +#include <stdint.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <arpa/inet.h> +#include <netinet/in6.h> + +static inline int hexval(int ch) +{ + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + return ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + return ch - 'a' + 10; + } else { + return -1; + } +} + +int inet_pton(int af, const char *src, void *dst) +{ + switch (af) { + case AF_INET: + return inet_aton(src, (struct in_addr *)dst); + + case AF_INET6: + { + struct in6_addr *d = (struct in6_addr *)dst; + int colons = 0, dcolons = 0; + int i; + const char *p; + + /* A double colon will increment colons by 2, + dcolons by 1 */ + for (p = dst; *p; p++) { + if (p[0] == ':') { + colons++; + if (p[1] == ':') + dcolons++; + } else if (!isxdigit(*p)) + return 0; /* Invalid address */ + } + + if (colons > 7 || dcolons > 1 + || (!dcolons && colons != 7)) + return 0; /* Invalid address */ + + memset(d, 0, sizeof(struct in6_addr)); + + i = 0; + for (p = dst; *p; p++) { + if (*p == ':') { + if (p[1] == ':') { + i += (8 - colons); + } else { + i++; + } + } else { + d->s6_addr16[i] = + htons((ntohs(d->s6_addr16[i]) << 4) + + hexval(*p)); + } + } + + return 1; + } + + default: + errno = EAFNOSUPPORT; + return -1; + } +} diff --git a/usr/klibc/isatty.c b/usr/klibc/isatty.c new file mode 100644 index 0000000..2359479 --- /dev/null +++ b/usr/klibc/isatty.c @@ -0,0 +1,16 @@ +/* + * isatty.c + */ + +#include <unistd.h> +#include <termios.h> +#include <errno.h> + +int isatty(int fd) +{ + struct termios dummy; + + /* All ttys support TIOCGPGRP */ + /* except /dev/console which needs TCGETS */ + return !ioctl(fd, TCGETS, &dummy); +} diff --git a/usr/klibc/jrand48.c b/usr/klibc/jrand48.c new file mode 100644 index 0000000..8e2b3ac --- /dev/null +++ b/usr/klibc/jrand48.c @@ -0,0 +1,24 @@ +/* + * jrand48.c + */ + +#include <stdlib.h> +#include <stdint.h> + +long jrand48(unsigned short xsubi[3]) +{ + uint64_t x; + + /* The xsubi[] array is littleendian by spec */ + x = (uint64_t) (uint16_t) xsubi[0] + + ((uint64_t) (uint16_t) xsubi[1] << 16) + + ((uint64_t) (uint16_t) xsubi[2] << 32); + + x = (0x5deece66dULL * x) + 0xb; + + xsubi[0] = (unsigned short)(uint16_t) x; + xsubi[1] = (unsigned short)(uint16_t) (x >> 16); + xsubi[2] = (unsigned short)(uint16_t) (x >> 32); + + return (long)(int32_t) (x >> 16); +} diff --git a/usr/klibc/lchown.c b/usr/klibc/lchown.c new file mode 100644 index 0000000..9a9e1e1 --- /dev/null +++ b/usr/klibc/lchown.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_lchown + +int lchown(const char *path, uid_t owner, gid_t group) +{ + return fchownat(AT_FDCWD, path, owner, group, AT_SYMLINK_NOFOLLOW); +} + +#endif /* __NR_lchown */ diff --git a/usr/klibc/libc_init.c b/usr/klibc/libc_init.c new file mode 100644 index 0000000..c5b9bab --- /dev/null +++ b/usr/klibc/libc_init.c @@ -0,0 +1,121 @@ +/* + * libc_init.c + * + * This function takes the raw data block set up by the ELF loader + * in the kernel and parses it. It is invoked by crt0.S which makes + * any necessary adjustments and passes calls this function using + * the standard C calling convention. + * + * The arguments are: + * uintptr_t *elfdata -- The ELF loader data block; usually from the stack. + * Basically a pointer to argc. + * void (*onexit)(void) -- Function to install into onexit + */ + +/* + * Several Linux ABIs don't pass the onexit pointer, and the ones that + * do never use it. Therefore, unless USE_ONEXIT is defined, we just + * ignore the onexit pointer. + */ +/* #define USE_ONEXIT */ + +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> +#include <klibc/compiler.h> +#include <elf.h> +#include <sys/auxv.h> +#include <klibc/sysconfig.h> +#include "atexit.h" + +#if _KLIBC_HAS_ARCHINIT +# include "klibc/archinit.h" +#else +# define __libc_archinit() ((void)0) +#endif + +/* This file is included from __static_init.c or __shared_init.c */ +#ifndef SHARED +# error "SHARED should be defined to 0 or 1" +#endif + +char **environ; +unsigned int __page_size, __page_shift; + +struct auxentry { + unsigned long type; + unsigned long v; +}; + +extern void __libc_init_stdio(void); + +unsigned long __auxval[_AUXVAL_MAX]; + +__noreturn __libc_init(uintptr_t * elfdata, void (*onexit) (void)) +{ + int argc; + char **argv, **envp, **envend; + struct auxentry *auxentry; +#if SHARED + typedef int (*main_t) (int, char **, char **); + main_t MAIN = NULL; +#else + extern int main(int, char **, char **); +#define MAIN main +#endif + unsigned int page_size = 0, page_shift = 0; + +#ifdef USE_ONEXIT + if (onexit) { + static struct atexit at_exit; + + at_exit.fctn = (void (*)(int, void *))onexit; + /* at_exit.next = NULL already */ + __atexit_list = &at_exit; + } +#else + (void)onexit; /* Ignore this... */ +#endif + + argc = (int)*elfdata++; + argv = (char **)elfdata; + envp = argv + (argc + 1); + + /* The auxillary entry vector is after all the environment vars */ + for (envend = envp; *envend; envend++) ; + auxentry = (struct auxentry *)(envend + 1); + + while (auxentry->type) { + if (auxentry->type < _AUXVAL_MAX) + __auxval[auxentry->type] = auxentry->v; + auxentry++; + } + +#if SHARED + MAIN = (main_t) __auxval[AT_ENTRY]; +#endif + + __page_size = page_size = __auxval[AT_PAGESZ]; + +#if __GNUC__ >= 4 + /* unsigned int is 32 bits on all our architectures */ + page_shift = __builtin_clz(page_size) ^ 31; +#elif defined(__i386__) || defined(__x86_64__) + asm("bsrl %1,%0" : "=r" (page_shift) : "r" (page_size)); +#else + while (page_size > 1) { + page_shift++; + page_size >>= 1; + } +#endif + __page_shift = page_shift; + +#if _KLIBC_HAS_ARCHINIT + __libc_archinit(); +#endif + + __libc_init_stdio(); + + environ = envp; + exit(MAIN(argc, argv, envp)); +} diff --git a/usr/klibc/libgcc/__ashldi3.c b/usr/klibc/libgcc/__ashldi3.c new file mode 100644 index 0000000..95937f0 --- /dev/null +++ b/usr/klibc/libgcc/__ashldi3.c @@ -0,0 +1,23 @@ +/* + * libgcc/__ashldi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +uint64_t __ashldi3(uint64_t v, int cnt) +{ + int c = cnt & 31; + uint32_t vl = (uint32_t) v; + uint32_t vh = (uint32_t) (v >> 32); + + if (cnt & 32) { + vh = (vl << c); + vl = 0; + } else { + vh = (vh << c) + (vl >> (32 - c)); + vl = (vl << c); + } + + return ((uint64_t) vh << 32) + vl; +} diff --git a/usr/klibc/libgcc/__ashrdi3.c b/usr/klibc/libgcc/__ashrdi3.c new file mode 100644 index 0000000..14e6d18 --- /dev/null +++ b/usr/klibc/libgcc/__ashrdi3.c @@ -0,0 +1,23 @@ +/* + * libgcc/__ashrdi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +uint64_t __ashrdi3(uint64_t v, int cnt) +{ + int c = cnt & 31; + uint32_t vl = (uint32_t) v; + uint32_t vh = (uint32_t) (v >> 32); + + if (cnt & 32) { + vl = ((int32_t) vh >> c); + vh = (int32_t) vh >> 31; + } else { + vl = (vl >> c) + (vh << (32 - c)); + vh = ((int32_t) vh >> c); + } + + return ((uint64_t) vh << 32) + vl; +} diff --git a/usr/klibc/libgcc/__clzsi2.c b/usr/klibc/libgcc/__clzsi2.c new file mode 100644 index 0000000..ebb11f0 --- /dev/null +++ b/usr/klibc/libgcc/__clzsi2.c @@ -0,0 +1,36 @@ +/* + * libgcc/__clzsi2.c + * + * Returns the leading number of 0 bits in the argument + */ + +#include <stdint.h> +#include <stddef.h> + +uint32_t __clzsi2(uint32_t v) +{ + int p = 31; + + if (v & 0xffff0000) { + p -= 16; + v >>= 16; + } + if (v & 0xff00) { + p -= 8; + v >>= 8; + } + if (v & 0xf0) { + p -= 4; + v >>= 4; + } + if (v & 0xc) { + p -= 2; + v >>= 2; + } + if (v & 0x2) { + p -= 1; + v >>= 1; + } + + return p; +} diff --git a/usr/klibc/libgcc/__divdi3.c b/usr/klibc/libgcc/__divdi3.c new file mode 100644 index 0000000..973fe63 --- /dev/null +++ b/usr/klibc/libgcc/__divdi3.c @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem); + +int64_t __divdi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if (num < 0) { + num = -num; + minus = 1; + } + if (den < 0) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, NULL); + if (minus) + v = -v; + + return v; +} diff --git a/usr/klibc/libgcc/__divsi3.c b/usr/klibc/libgcc/__divsi3.c new file mode 100644 index 0000000..35420f5 --- /dev/null +++ b/usr/klibc/libgcc/__divsi3.c @@ -0,0 +1,29 @@ +/* + * libgcc/__divsi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +extern uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem); + +int32_t __divsi3(int32_t num, int32_t den) +{ + int minus = 0; + int32_t v; + + if (num < 0) { + num = -num; + minus = 1; + } + if (den < 0) { + den = -den; + minus ^= 1; + } + + v = __udivmodsi4(num, den, NULL); + if (minus) + v = -v; + + return v; +} diff --git a/usr/klibc/libgcc/__lshrdi3.c b/usr/klibc/libgcc/__lshrdi3.c new file mode 100644 index 0000000..765e1f2 --- /dev/null +++ b/usr/klibc/libgcc/__lshrdi3.c @@ -0,0 +1,23 @@ +/* + * libgcc/__lshrdi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +uint64_t __lshrdi3(uint64_t v, int cnt) +{ + int c = cnt & 31; + uint32_t vl = (uint32_t) v; + uint32_t vh = (uint32_t) (v >> 32); + + if (cnt & 32) { + vl = (vh >> c); + vh = 0; + } else { + vl = (vl >> c) + (vh << (32 - c)); + vh = (vh >> c); + } + + return ((uint64_t) vh << 32) + vl; +} diff --git a/usr/klibc/libgcc/__moddi3.c b/usr/klibc/libgcc/__moddi3.c new file mode 100644 index 0000000..0e7ed98 --- /dev/null +++ b/usr/klibc/libgcc/__moddi3.c @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__moddi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem); + +int64_t __moddi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if (num < 0) { + num = -num; + minus = 1; + } + if (den < 0) { + den = -den; + minus ^= 1; + } + + (void)__udivmoddi4(num, den, (uint64_t *) & v); + if (minus) + v = -v; + + return v; +} diff --git a/usr/klibc/libgcc/__modsi3.c b/usr/klibc/libgcc/__modsi3.c new file mode 100644 index 0000000..33a21ba --- /dev/null +++ b/usr/klibc/libgcc/__modsi3.c @@ -0,0 +1,29 @@ +/* + * libgcc/__modsi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +extern uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem); + +int32_t __modsi3(int32_t num, int32_t den) +{ + int minus = 0; + int32_t v; + + if (num < 0) { + num = -num; + minus = 1; + } + if (den < 0) { + den = -den; + minus ^= 1; + } + + (void)__udivmodsi4(num, den, (uint32_t *) & v); + if (minus) + v = -v; + + return v; +} diff --git a/usr/klibc/libgcc/__udivdi3.c b/usr/klibc/libgcc/__udivdi3.c new file mode 100644 index 0000000..5eea461 --- /dev/null +++ b/usr/klibc/libgcc/__udivdi3.c @@ -0,0 +1,13 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem); + +uint64_t __udivdi3(uint64_t num, uint64_t den) +{ + return __udivmoddi4(num, den, NULL); +} diff --git a/usr/klibc/libgcc/__udivmoddi4.c b/usr/klibc/libgcc/__udivmoddi4.c new file mode 100644 index 0000000..aa86112 --- /dev/null +++ b/usr/klibc/libgcc/__udivmoddi4.c @@ -0,0 +1,32 @@ +#include <klibc/diverr.h> +#include <stdint.h> + +uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem_p) +{ + uint64_t quot = 0, qbit = 1; + + if (den == 0) { + __divide_error(); + return 0; /* If trap returns... */ + } + + /* Left-justify denominator and count shift */ + while ((int64_t) den >= 0) { + den <<= 1; + qbit <<= 1; + } + + while (qbit) { + if (den <= num) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if (rem_p) + *rem_p = num; + + return quot; +} diff --git a/usr/klibc/libgcc/__udivmodsi4.c b/usr/klibc/libgcc/__udivmodsi4.c new file mode 100644 index 0000000..54980f0 --- /dev/null +++ b/usr/klibc/libgcc/__udivmodsi4.c @@ -0,0 +1,32 @@ +#include <klibc/diverr.h> +#include <stdint.h> + +uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem_p) +{ + uint32_t quot = 0, qbit = 1; + + if (den == 0) { + __divide_error(); + return 0; /* If trap returns... */ + } + + /* Left-justify denominator and count shift */ + while ((int32_t) den >= 0) { + den <<= 1; + qbit <<= 1; + } + + while (qbit) { + if (den <= num) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if (rem_p) + *rem_p = num; + + return quot; +} diff --git a/usr/klibc/libgcc/__udivsi3.c b/usr/klibc/libgcc/__udivsi3.c new file mode 100644 index 0000000..5635f3f --- /dev/null +++ b/usr/klibc/libgcc/__udivsi3.c @@ -0,0 +1,13 @@ +/* + * libgcc/__divsi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +extern uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem); + +uint32_t __udivsi3(uint32_t num, uint32_t den) +{ + return __udivmodsi4(num, den, NULL); +} diff --git a/usr/klibc/libgcc/__umoddi3.c b/usr/klibc/libgcc/__umoddi3.c new file mode 100644 index 0000000..1fc754a --- /dev/null +++ b/usr/klibc/libgcc/__umoddi3.c @@ -0,0 +1,16 @@ +/* + * arch/i386/libgcc/__umoddi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem); + +uint64_t __umoddi3(uint64_t num, uint64_t den) +{ + uint64_t v; + + (void)__udivmoddi4(num, den, &v); + return v; +} diff --git a/usr/klibc/libgcc/__umodsi3.c b/usr/klibc/libgcc/__umodsi3.c new file mode 100644 index 0000000..85e6e3c --- /dev/null +++ b/usr/klibc/libgcc/__umodsi3.c @@ -0,0 +1,16 @@ +/* + * libgcc/__umodsi3.c + */ + +#include <stdint.h> +#include <stddef.h> + +extern uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem); + +uint32_t __umodsi3(uint32_t num, uint32_t den) +{ + uint32_t v; + + (void)__udivmodsi4(num, den, &v); + return v; +} diff --git a/usr/klibc/link.c b/usr/klibc/link.c new file mode 100644 index 0000000..1d4b70e --- /dev/null +++ b/usr/klibc/link.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_link + +int link(const char *oldpath, const char *newpath) +{ + return linkat(AT_FDCWD, oldpath, AT_FDCWD, newpath, 0); +} + +#endif /* __NR_link */ diff --git a/usr/klibc/lrand48.c b/usr/klibc/lrand48.c new file mode 100644 index 0000000..7dfcf92 --- /dev/null +++ b/usr/klibc/lrand48.c @@ -0,0 +1,13 @@ +/* + * lrand48.c + */ + +#include <stdlib.h> +#include <stdint.h> + +unsigned short __rand48_seed[3]; /* Common with mrand48.c, srand48.c */ + +long lrand48(void) +{ + return (uint32_t) jrand48(__rand48_seed) >> 1; +} diff --git a/usr/klibc/lseek.c b/usr/klibc/lseek.c new file mode 100644 index 0000000..f262d19 --- /dev/null +++ b/usr/klibc/lseek.c @@ -0,0 +1,31 @@ +/* + * lseek.c + * + * On 32-bit platforms, we need to use the _llseek() system call + * rather than lseek(), to be able to handle large disks. _llseek() + * isn't just a normal syscall which takes a 64-bit argument; it needs + * to return a 64-bit value and so takes an extra pointer. + */ + +#include <unistd.h> +#include <sys/syscall.h> +#include <bitsize.h> + +#if _BITSIZE == 32 + +extern int __llseek(int fd, unsigned long hi, unsigned long lo, off_t * res, + int whence); + +off_t lseek(int fd, off_t offset, int whence) +{ + unsigned long long ullo = offset; + off_t result; + int rv; + + rv = __llseek(fd, (unsigned long)(ullo >> 32), (unsigned long)ullo, + &result, whence); + + return rv ? (off_t)-1 : result; +} + +#endif diff --git a/usr/klibc/lstat.c b/usr/klibc/lstat.c new file mode 100644 index 0000000..3e8146f --- /dev/null +++ b/usr/klibc/lstat.c @@ -0,0 +1,10 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syscall.h> + +int lstat(const char *path, struct stat *buf) +{ + return fstatat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); +} diff --git a/usr/klibc/makeerrlist.pl b/usr/klibc/makeerrlist.pl new file mode 100644 index 0000000..43c747c --- /dev/null +++ b/usr/klibc/makeerrlist.pl @@ -0,0 +1,98 @@ +#!/usr/bin/perl +# +# This creates sys_errlist from <asm/errno.h> through somewhat +# heuristic matching. It presumes the relevant entries are of the form +# #define Exxxx <integer> /* comment */ +# + +use FileHandle; + +%errors = (); +%errmsg = (); +$maxerr = -1; +@includelist = (); # Include directories + +sub parse_file($) { + my($file) = @_; + my($fh) = new FileHandle; + my($line, $error, $msg); + my($kernelonly) = 0; + my($root); + + print STDERR "opening $file\n" unless ( $quiet ); + + $ok = 0; + foreach $root ( @includelist ) { + if ( $fh->open($root.'//'.$file, '<') ) { + $ok = 1; + last; + } + } + + if ( ! $ok ) { + die "$0: Cannot find file $file\n"; + } + + while ( defined($line = <$fh>) ) { + if ( $kernelonly ) { + if ( $line =~ /^\#\s*endif/ ) { + $kernelonly--; + } elsif ( $line =~ /^\#\sif/ ) { + $kernelonly++; + } + } else { + if ( $line =~ /^\#\s*define\s+([A-Z0-9_]+)\s+([0-9]+)\s*\/\*\s*(.*\S)\s*\*\// ) { + $error = $1; + $errno = $2+0; + $msg = $3; + print STDERR "$error ($errno) => \"$msg\"\n" unless ( $quiet ); + $errors{$errno} = $error; + $errmsg{$errno} = $msg; + $maxerr = $errno if ( $errno > $maxerr ); + } elsif ( $line =~ /^\#\s*include\s+[\<\"](.*)[\>\"]/ ) { + parse_file($1); + } elsif ( $line =~ /^\#\s*ifdef\s+__KERNEL__/ ) { + $kernelonly++; + } + } + } + close($fh); + print STDERR "closing $file\n" unless ( $quiet ); +} + +$v = $ENV{'KBUILD_VERBOSE'}; +$quiet = defined($v) ? !$v : 0; + +foreach $arg ( @ARGV ) { + if ( $arg eq '-q' ) { + $quiet = 1; + } elsif ( $arg =~ /^-(errlist|errnos|maxerr)$/ ) { + $type = $arg; + } elsif ( $arg =~ '^\-I' ) { + push(@includelist, "$'"); + } else { + # Ignore + } +} + +parse_file('linux/errno.h'); + +if ( $type eq '-errlist' ) { + print "#include <errno.h>\n"; + printf "const int sys_nerr = %d;\n", $maxerr+1; + printf "const char * const sys_errlist[%d] = {\n", $maxerr+1; + foreach $e ( sort(keys(%errors)) ) { + printf " [%s] = \"%s\",\n", $errors{$e}, $errmsg{$e}; + } + print "};\n"; +} elsif ( $type eq '-errnos' ) { + print "#include <errno.h>\n"; + printf "const int sys_nerr = %d;\n", $maxerr+1; + printf "const char * const sys_errlist[%d] = {\n", $maxerr+1; + foreach $e ( sort(keys(%errors)) ) { + printf " [%s] = \"%s\",\n", $errors{$e}, $errors{$e}; + } + print "};\n"; +} elsif ( $type eq '-maxerr' ) { + print $maxerr, "\n"; +} diff --git a/usr/klibc/malloc.c b/usr/klibc/malloc.c new file mode 100644 index 0000000..09a596f --- /dev/null +++ b/usr/klibc/malloc.c @@ -0,0 +1,299 @@ +/* + * malloc.c + * + * Very simple linked-list based malloc()/free(). + */ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> +#include <assert.h> +#include <errno.h> +#include "malloc.h" + +/* Both the arena list and the free memory list are double linked + list with head node. This the head node. Note that the arena list + is sorted in order of address. */ +static struct free_arena_header __malloc_head = { + { + ARENA_TYPE_HEAD, + 0, + &__malloc_head, + &__malloc_head, + }, + &__malloc_head, + &__malloc_head +}; + +static inline void mark_block_dead(struct free_arena_header *ah) +{ +#ifdef DEBUG_MALLOC + ah->a.type = ARENA_TYPE_DEAD; +#endif +} + +static inline void remove_from_main_chain(struct free_arena_header *ah) +{ + struct free_arena_header *ap, *an; + + mark_block_dead(ah); + + ap = ah->a.prev; + an = ah->a.next; + ap->a.next = an; + an->a.prev = ap; +} + +static inline void remove_from_free_chain(struct free_arena_header *ah) +{ + struct free_arena_header *ap, *an; + + ap = ah->prev_free; + an = ah->next_free; + ap->next_free = an; + an->prev_free = ap; +} + +static inline void remove_from_chains(struct free_arena_header *ah) +{ + remove_from_free_chain(ah); + remove_from_main_chain(ah); +} + +static void *__malloc_from_block(struct free_arena_header *fp, size_t size) +{ + size_t fsize; + struct free_arena_header *nfp, *na, *fpn, *fpp; + + fsize = fp->a.size; + + /* We need the 2* to account for the larger requirements of a + free block */ + if (fsize >= size + 2 * sizeof(struct arena_header)) { + /* Bigger block than required -- split block */ + nfp = (struct free_arena_header *)((char *)fp + size); + na = fp->a.next; + + nfp->a.type = ARENA_TYPE_FREE; + nfp->a.size = fsize - size; + fp->a.type = ARENA_TYPE_USED; + fp->a.size = size; + + /* Insert into all-block chain */ + nfp->a.prev = fp; + nfp->a.next = na; + na->a.prev = nfp; + fp->a.next = nfp; + + /* Replace current block on free chain */ + nfp->next_free = fpn = fp->next_free; + nfp->prev_free = fpp = fp->prev_free; + fpn->prev_free = nfp; + fpp->next_free = nfp; + } else { + fp->a.type = ARENA_TYPE_USED; /* Allocate the whole block */ + remove_from_free_chain(fp); + } + + return (void *)(&fp->a + 1); +} + +static struct free_arena_header *__free_block(struct free_arena_header *ah) +{ + struct free_arena_header *pah, *nah; + + pah = ah->a.prev; + nah = ah->a.next; + if (pah->a.type == ARENA_TYPE_FREE && + (char *)pah + pah->a.size == (char *)ah) { + /* Coalesce into the previous block */ + pah->a.size += ah->a.size; + pah->a.next = nah; + nah->a.prev = pah; + mark_block_dead(ah); + + ah = pah; + pah = ah->a.prev; + } else { + /* Need to add this block to the free chain */ + ah->a.type = ARENA_TYPE_FREE; + + ah->next_free = __malloc_head.next_free; + ah->prev_free = &__malloc_head; + __malloc_head.next_free = ah; + ah->next_free->prev_free = ah; + } + + /* In either of the previous cases, we might be able to merge + with the subsequent block... */ + if (nah->a.type == ARENA_TYPE_FREE && + (char *)ah + ah->a.size == (char *)nah) { + ah->a.size += nah->a.size; + + /* Remove the old block from the chains */ + remove_from_chains(nah); + } + + /* Return the block that contains the called block */ + return ah; +} + +void *malloc(size_t size) +{ + struct free_arena_header *fp; + struct free_arena_header *pah; + size_t fsize; + + if (size == 0) + return NULL; + + /* Various additions below will overflow if size is close to + SIZE_MAX. Further, it's not legal for a C object to be + larger than PTRDIFF_MAX (half of SIZE_MAX) as pointer + arithmetic within it could overflow. */ + if (size > PTRDIFF_MAX) { + errno = ENOMEM; + return NULL; + } + + /* Add the obligatory arena header, and round up */ + size = (size + 2 * sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK; + + for (fp = __malloc_head.next_free; fp->a.type != ARENA_TYPE_HEAD; + fp = fp->next_free) { + if (fp->a.size >= size) { + /* Found fit -- allocate out of this block */ + return __malloc_from_block(fp, size); + } + } + + /* Nothing found... need to request a block from the kernel */ + fsize = (size + MALLOC_CHUNK_MASK) & ~MALLOC_CHUNK_MASK; + +#if _KLIBC_MALLOC_USES_SBRK + if (fsize > INTPTR_MAX) { + errno = ENOMEM; + return NULL; + } + fp = (struct free_arena_header *)sbrk(fsize); +#else + fp = (struct free_arena_header *) + mmap(NULL, fsize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); +#endif + + if (fp == (struct free_arena_header *)MAP_FAILED) { + errno = ENOMEM; + return NULL; /* Failed to get a block */ + } + + /* Insert the block into the management chains. We need to set + up the size and the main block list pointer, the rest of + the work is logically identical to free(). */ + fp->a.type = ARENA_TYPE_FREE; + fp->a.size = fsize; + + /* We need to insert this into the main block list in the proper + place -- this list is required to be sorted. Since we most likely + get memory assignments in ascending order, search backwards for + the proper place. */ + for (pah = __malloc_head.a.prev; pah->a.type != ARENA_TYPE_HEAD; + pah = pah->a.prev) { + if (pah < fp) + break; + } + + /* Now pah points to the node that should be the predecessor of + the new node */ + fp->a.next = pah->a.next; + fp->a.prev = pah; + pah->a.next = fp; + fp->a.next->a.prev = fp; + + /* Insert into the free chain and coalesce with adjacent blocks */ + fp = __free_block(fp); + + /* Now we can allocate from this block */ + return __malloc_from_block(fp, size); +} + +void free(void *ptr) +{ + struct free_arena_header *ah; + + if (!ptr) + return; + + ah = (struct free_arena_header *) + ((struct arena_header *)ptr - 1); + +#ifdef DEBUG_MALLOC + assert(ah->a.type == ARENA_TYPE_USED); +#endif + + /* Merge into adjacent free blocks */ + ah = __free_block(ah); + + /* See if it makes sense to return memory to the system */ +#if _KLIBC_MALLOC_USES_SBRK + if (ah->a.size >= _KLIBC_MALLOC_CHUNK_SIZE && + (char *)ah + ah->a.size == __current_brk) { + remove_from_chains(ah); + brk(ah); + } +#else + { + size_t page_size = getpagesize(); + size_t page_mask = page_size - 1; + size_t head_portion = -(size_t)ah & page_mask; + size_t tail_portion = ((size_t)ah + ah->a.size) & page_mask; + size_t adj_size; + + /* Careful here... an individual chunk of memory must have + a minimum size if it exists at all, so if either the + head or the tail is below the minimum, then extend + that chunk by a page. */ + + if (head_portion && + head_portion < 2*sizeof(struct arena_header)) + head_portion += page_size; + + if (tail_portion && + tail_portion < 2*sizeof(struct arena_header)) + tail_portion += page_size; + + adj_size = ah->a.size - head_portion - tail_portion; + + /* Worth it? This is written the way it is to guard + against overflows... */ + if (ah->a.size >= head_portion+tail_portion+ + _KLIBC_MALLOC_CHUNK_SIZE) { + struct free_arena_header *tah, *tan, *tap; + + if (tail_portion) { + /* Make a new header, and insert into chains + immediately after the current block */ + tah = (struct free_arena_header *) + ((char *)ah + head_portion + adj_size); + tah->a.type = ARENA_TYPE_FREE; + tah->a.size = tail_portion; + tah->a.next = tan = ah->a.next; + tan->a.prev = tah; + tah->a.prev = ah; + ah->a.next = tah; + tah->prev_free = tap = ah->prev_free; + tap->next_free = tah; + tah->next_free = ah; + ah->prev_free = tah; + } + + if (head_portion) + ah->a.size = head_portion; + else + remove_from_chains(ah); + + munmap((char *)ah + head_portion, adj_size); + } + } +#endif +} diff --git a/usr/klibc/malloc.h b/usr/klibc/malloc.h new file mode 100644 index 0000000..af97a3f --- /dev/null +++ b/usr/klibc/malloc.h @@ -0,0 +1,50 @@ +/* + * malloc.h + * + * Internals for the memory allocator + */ + +#include <stdint.h> +#include <stddef.h> +#include <klibc/sysconfig.h> + +/* + * This structure should be a power of two. This becomes the + * alignment unit. + */ +struct free_arena_header; + +struct arena_header { + size_t type; + size_t size; + struct free_arena_header *next, *prev; +}; + +#ifdef DEBUG_MALLOC +#define ARENA_TYPE_USED 0x64e69c70 +#define ARENA_TYPE_FREE 0x012d610a +#define ARENA_TYPE_HEAD 0x971676b5 +#define ARENA_TYPE_DEAD 0xeeeeeeee +#else +#define ARENA_TYPE_USED 0 +#define ARENA_TYPE_FREE 1 +#define ARENA_TYPE_HEAD 2 +#endif + +#define MALLOC_CHUNK_MASK (_KLIBC_MALLOC_CHUNK_SIZE-1) + +#define ARENA_SIZE_MASK (~(sizeof(struct arena_header)-1)) + +/* + * This structure should be no more than twice the size of the + * previous structure. + */ +struct free_arena_header { + struct arena_header a; + struct free_arena_header *next_free, *prev_free; +}; + +/* + * Internal variable used by brk/sbrk + */ +extern char *__current_brk; diff --git a/usr/klibc/memccpy.c b/usr/klibc/memccpy.c new file mode 100644 index 0000000..83d02c9 --- /dev/null +++ b/usr/klibc/memccpy.c @@ -0,0 +1,23 @@ +/* + * memccpy.c + * + * memccpy() + */ + +#include <stddef.h> +#include <string.h> + +void *memccpy(void *dst, const void *src, int c, size_t n) +{ + char *q = dst; + const char *p = src; + char ch; + + while (n--) { + *q++ = ch = *p++; + if (ch == (char)c) + return q; + } + + return NULL; /* No instance of "c" found */ +} diff --git a/usr/klibc/memchr.c b/usr/klibc/memchr.c new file mode 100644 index 0000000..f1947fb --- /dev/null +++ b/usr/klibc/memchr.c @@ -0,0 +1,19 @@ +/* + * memchr.c + */ + +#include <stddef.h> +#include <string.h> + +void *memchr(const void *s, int c, size_t n) +{ + const unsigned char *sp = s; + + while (n--) { + if (*sp == (unsigned char)c) + return (void *)sp; + sp++; + } + + return NULL; +} diff --git a/usr/klibc/memcmp.c b/usr/klibc/memcmp.c new file mode 100644 index 0000000..3ce9941 --- /dev/null +++ b/usr/klibc/memcmp.c @@ -0,0 +1,19 @@ +/* + * memcmp.c + */ + +#include <string.h> + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *c1 = s1, *c2 = s2; + int d = 0; + + while (n--) { + d = (int)*c1++ - (int)*c2++; + if (d) + break; + } + + return d; +} diff --git a/usr/klibc/memcpy.c b/usr/klibc/memcpy.c new file mode 100644 index 0000000..5ce206d --- /dev/null +++ b/usr/klibc/memcpy.c @@ -0,0 +1,29 @@ +/* + * memcpy.c + */ + +#include <string.h> +#include <stdint.h> + +void *memcpy(void *dst, const void *src, size_t n) +{ + const char *p = src; + char *q = dst; +#if defined(__i386__) + size_t nl = n >> 2; + asm volatile ("cld ; rep ; movsl ; movl %3,%0 ; rep ; movsb":"+c" (nl), + "+S"(p), "+D"(q) + :"r"(n & 3)); +#elif defined(__x86_64__) + size_t nq = n >> 3; + asm volatile ("cld ; rep ; movsq ; movl %3,%%ecx ; rep ; movsb":"+c" + (nq), "+S"(p), "+D"(q) + :"r"((uint32_t) (n & 7))); +#else + while (n--) { + *q++ = *p++; + } +#endif + + return dst; +} diff --git a/usr/klibc/memmem.c b/usr/klibc/memmem.c new file mode 100644 index 0000000..72422aa --- /dev/null +++ b/usr/klibc/memmem.c @@ -0,0 +1,55 @@ +/* + * memmem.c + * + * Find a byte string inside a longer byte string + * + * This uses the "Not So Naive" algorithm, a very simple but + * usually effective algorithm, see: + * + * http://www-igm.univ-mlv.fr/~lecroq/string/ + */ + +#include <string.h> + +void *memmem(const void *haystack, size_t n, const void *needle, size_t m) +{ + const unsigned char *y = (const unsigned char *)haystack; + const unsigned char *x = (const unsigned char *)needle; + + size_t j, k, l; + + if (!m) + return (void *)haystack; + + if (m > n) + return NULL; + + if (1 != m) { + if (x[0] == x[1]) { + k = 2; + l = 1; + } else { + k = 1; + l = 2; + } + + j = 0; + while (j <= n - m) { + if (x[1] != y[j + 1]) { + j += k; + } else { + if (!memcmp(x + 2, y + j + 2, m - 2) + && x[0] == y[j]) + return (void *)&y[j]; + j += l; + } + } + } else + do { + if (*y == *x) + return (void *)y; + y++; + } while (--n); + + return NULL; +} diff --git a/usr/klibc/memmove.c b/usr/klibc/memmove.c new file mode 100644 index 0000000..a398cd8 --- /dev/null +++ b/usr/klibc/memmove.c @@ -0,0 +1,36 @@ +/* + * memmove.c + */ + +#include <string.h> + +void *memmove(void *dst, const void *src, size_t n) +{ + const char *p = src; + char *q = dst; +#if defined(__i386__) || defined(__x86_64__) + if (q < p) { + asm volatile("cld; rep; movsb" + : "+c" (n), "+S"(p), "+D"(q)); + } else { + p += (n - 1); + q += (n - 1); + asm volatile("std; rep; movsb; cld" + : "+c" (n), "+S"(p), "+D"(q)); + } +#else + if (q < p) { + while (n--) { + *q++ = *p++; + } + } else { + p += n; + q += n; + while (n--) { + *--q = *--p; + } + } +#endif + + return dst; +} diff --git a/usr/klibc/memrchr.c b/usr/klibc/memrchr.c new file mode 100644 index 0000000..ff6d711 --- /dev/null +++ b/usr/klibc/memrchr.c @@ -0,0 +1,19 @@ +/* + * memrchr.c + */ + +#include <stddef.h> +#include <string.h> + +void *memrchr(const void *s, int c, size_t n) +{ + const unsigned char *sp = (const unsigned char *)s + n - 1; + + while (n--) { + if (*sp == (unsigned char)c) + return (void *)sp; + sp--; + } + + return NULL; +} diff --git a/usr/klibc/memset.c b/usr/klibc/memset.c new file mode 100644 index 0000000..aa00b5b --- /dev/null +++ b/usr/klibc/memset.c @@ -0,0 +1,30 @@ +/* + * memset.c + */ + +#include <string.h> +#include <stdint.h> + +void *memset(void *dst, int c, size_t n) +{ + char *q = dst; + +#if defined(__i386__) + size_t nl = n >> 2; + asm volatile ("cld ; rep ; stosl ; movl %3,%0 ; rep ; stosb" + : "+c" (nl), "+D" (q) + : "a" ((unsigned char)c * 0x01010101U), "r" (n & 3)); +#elif defined(__x86_64__) + size_t nq = n >> 3; + asm volatile ("cld ; rep ; stosq ; movl %3,%%ecx ; rep ; stosb" + :"+c" (nq), "+D" (q) + : "a" ((unsigned char)c * 0x0101010101010101U), + "r" ((uint32_t) n & 7)); +#else + while (n--) { + *q++ = c; + } +#endif + + return dst; +} diff --git a/usr/klibc/memswap.c b/usr/klibc/memswap.c new file mode 100644 index 0000000..b32315c --- /dev/null +++ b/usr/klibc/memswap.c @@ -0,0 +1,24 @@ +/* + * memswap() + * + * Swaps the contents of two nonoverlapping memory areas. + * This really could be done faster... + */ + +#include <string.h> + +void memswap(void *m1, void *m2, size_t n) +{ + char *p = m1; + char *q = m2; + char tmp; + + while (n--) { + tmp = *p; + *p = *q; + *q = tmp; + + p++; + q++; + } +} diff --git a/usr/klibc/mkdir.c b/usr/klibc/mkdir.c new file mode 100644 index 0000000..27673e3 --- /dev/null +++ b/usr/klibc/mkdir.c @@ -0,0 +1,14 @@ +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_mkdir + +int mkdir(const char *pathname, mode_t mode) +{ + return mkdirat(AT_FDCWD, pathname, mode); +} + +#endif /* __NR_mkdir */ diff --git a/usr/klibc/mknod.c b/usr/klibc/mknod.c new file mode 100644 index 0000000..727505f --- /dev/null +++ b/usr/klibc/mknod.c @@ -0,0 +1,14 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_mknod + +int mknod(const char *pathname, mode_t mode, dev_t dev) +{ + return mknodat(AT_FDCWD, pathname, mode, dev); +} + +#endif /* __NR_mknod */ diff --git a/usr/klibc/mmap.c b/usr/klibc/mmap.c new file mode 100644 index 0000000..2c427ca --- /dev/null +++ b/usr/klibc/mmap.c @@ -0,0 +1,40 @@ +/* + * mmap.c + */ + +#include <stdint.h> +#include <errno.h> +#include <sys/syscall.h> +#include <sys/mman.h> +#include <unistd.h> +#include <bitsize.h> +#include <klibc/sysconfig.h> + +/* + * Set in SYSCALLS whether or not we should use an unadorned mmap() system + * call (typical on 64-bit architectures). + */ +#if _KLIBC_USE_MMAP2 + +/* This architecture uses mmap2(). The Linux mmap2() system call takes + a page offset as the offset argument. We need to make sure we have + the proper conversion in place. */ + +extern void *__mmap2(void *, size_t, int, int, int, size_t); + +void *mmap(void *start, size_t length, int prot, int flags, int fd, + off_t offset) +{ + const int mmap2_shift = _KLIBC_MMAP2_SHIFT; + const off_t mmap2_mask = ((off_t) 1 << mmap2_shift) - 1; + + if (offset & mmap2_mask) { + errno = EINVAL; + return MAP_FAILED; + } + + return __mmap2(start, length, prot, flags, fd, + (size_t) offset >> mmap2_shift); +} + +#endif diff --git a/usr/klibc/mrand48.c b/usr/klibc/mrand48.c new file mode 100644 index 0000000..e3b73cc --- /dev/null +++ b/usr/klibc/mrand48.c @@ -0,0 +1,13 @@ +/* + * mrand48.c + */ + +#include <stdlib.h> +#include <stdint.h> + +unsigned short __rand48_seed[3]; /* Common with lrand48.c, srand48.c */ + +long mrand48(void) +{ + return jrand48(__rand48_seed); +} diff --git a/usr/klibc/nanosleep.c b/usr/klibc/nanosleep.c new file mode 100644 index 0000000..1141219 --- /dev/null +++ b/usr/klibc/nanosleep.c @@ -0,0 +1,8 @@ +#include <time.h> +#include <sys/time.h> +#include <sys/syscall.h> + +int nanosleep(const struct timespec *request, struct timespec *remain) +{ + return clock_nanosleep(CLOCK_MONOTONIC, 0, request, remain) ? -1 : 0; +} diff --git a/usr/klibc/nice.c b/usr/klibc/nice.c new file mode 100644 index 0000000..e6e99ac --- /dev/null +++ b/usr/klibc/nice.c @@ -0,0 +1,19 @@ +/* + * nice.c + */ + +#include <unistd.h> +#include <sched.h> +#include <sys/resource.h> +#include <sys/syscall.h> + +#ifndef __NR_nice + +int nice(int inc) +{ + pid_t me = getpid(); + return setpriority(me, PRIO_PROCESS, + getpriority(me, PRIO_PROCESS) + inc); +} + +#endif diff --git a/usr/klibc/nrand48.c b/usr/klibc/nrand48.c new file mode 100644 index 0000000..cb3532b --- /dev/null +++ b/usr/klibc/nrand48.c @@ -0,0 +1,11 @@ +/* + * nrand48.c + */ + +#include <stdlib.h> +#include <stdint.h> + +long nrand48(unsigned short xsubi[3]) +{ + return (long)((uint32_t) jrand48(xsubi) >> 1); +} diff --git a/usr/klibc/nullenv.c b/usr/klibc/nullenv.c new file mode 100644 index 0000000..ba7e71c --- /dev/null +++ b/usr/klibc/nullenv.c @@ -0,0 +1,8 @@ +/* + * nullenv.c + */ + +#include <stddef.h> +#include "env.h" + +char * const __null_environ[] = { NULL }; diff --git a/usr/klibc/onexit.c b/usr/klibc/onexit.c new file mode 100644 index 0000000..15a96b5 --- /dev/null +++ b/usr/klibc/onexit.c @@ -0,0 +1,23 @@ +/* + * onexit.c + */ + +#include <stdlib.h> +#include <unistd.h> +#include "atexit.h" + +int on_exit(void (*fctn) (int, void *), void *arg) +{ + struct atexit *as = malloc(sizeof(struct atexit)); + + if (!as) + return -1; + + as->fctn = fctn; + as->arg = arg; + + as->next = __atexit_list; + __atexit_list = as; + + return 0; +} diff --git a/usr/klibc/open.c b/usr/klibc/open.c new file mode 100644 index 0000000..5305c3d --- /dev/null +++ b/usr/klibc/open.c @@ -0,0 +1,47 @@ +/* + * open.c + * + * On 32-bit platforms we need to pass O_LARGEFILE to the open() + * system call, to indicate that we're 64-bit safe. + * + * For 64 bit systems without the open syscall, pass straight + * through into openat. + */ + +#define _KLIBC_IN_OPEN_C +#include <unistd.h> +#include <fcntl.h> +#include <bitsize.h> +#include <sys/syscall.h> + +#ifndef __NR_open +#if _BITSIZE == 32 + +extern int __openat(int, const char *, int, mode_t); + +int open(const char *pathname, int flags, mode_t mode) +{ + return __openat(AT_FDCWD, pathname, flags | O_LARGEFILE, mode); +} + +#else + +__extern int openat(int, const char *, int, ...); + +int open(const char *pathname, int flags, mode_t mode) +{ + return openat(AT_FDCWD, pathname, flags, mode); +} + +#endif /* _BITSIZE == 32 */ + +#elif _BITSIZE == 32 && !defined(__i386__) && !defined(__m68k__) + +extern int __open(const char *, int, mode_t); + +int open(const char *pathname, int flags, mode_t mode) +{ + return __open(pathname, flags | O_LARGEFILE, mode); +} + +#endif /* __NR_open */ diff --git a/usr/klibc/openat.c b/usr/klibc/openat.c new file mode 100644 index 0000000..8e5baa0 --- /dev/null +++ b/usr/klibc/openat.c @@ -0,0 +1,22 @@ +/* + * openat.c + * + * On 32-bit platforms we need to pass O_LARGEFILE to the openat() + * system call, to indicate that we're 64-bit safe. + */ + +#define _KLIBC_IN_OPEN_C +#include <unistd.h> +#include <fcntl.h> +#include <bitsize.h> + +#if _BITSIZE == 32 && !defined(__i386__) && !defined(__m68k__) && defined(__NR_openat) + +extern int __openat(int, const char *, int, mode_t); + +int openat(int dirfd, const char *pathname, int flags, mode_t mode) +{ + return __openat(dirfd, pathname, flags | O_LARGEFILE, mode); +} + +#endif diff --git a/usr/klibc/pause.c b/usr/klibc/pause.c new file mode 100644 index 0000000..cec97a8 --- /dev/null +++ b/usr/klibc/pause.c @@ -0,0 +1,17 @@ +/* + * pause.c + */ + +#include <stddef.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/syscall.h> + +#ifndef __NR_pause + +int pause(void) +{ + return select(0, NULL, NULL, NULL, NULL); +} + +#endif diff --git a/usr/klibc/perror.c b/usr/klibc/perror.c new file mode 100644 index 0000000..3177057 --- /dev/null +++ b/usr/klibc/perror.c @@ -0,0 +1,13 @@ +/* + * perror.c + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +void perror(const char *s) +{ + int e = errno; + fprintf(stderr, "%s: %s\n", s, strerror(e)); +} diff --git a/usr/klibc/pipe.c b/usr/klibc/pipe.c new file mode 100644 index 0000000..dfaed9e --- /dev/null +++ b/usr/klibc/pipe.c @@ -0,0 +1,11 @@ +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_pipe + +int pipe(int pipefd[2]) +{ + return pipe2(pipefd, 0); +} + +#endif /* __NR_pipe */ diff --git a/usr/klibc/poll.c b/usr/klibc/poll.c new file mode 100644 index 0000000..f539b99 --- /dev/null +++ b/usr/klibc/poll.c @@ -0,0 +1,21 @@ +#include <errno.h> +#include <sys/poll.h> +#include <sys/syscall.h> + +#ifndef __NR_poll + +int poll(struct pollfd *fds, nfds_t nfds, long timeout) +{ + struct timespec timeout_ts; + struct timespec *timeout_ts_p = NULL; + + if (timeout >= 0) { + timeout_ts.tv_sec = timeout / 1000; + timeout_ts.tv_nsec = (timeout % 1000) * 1000000; + timeout_ts_p = &timeout_ts; + } + + return ppoll(fds, nfds, timeout_ts_p, 0); +} + +#endif /* __NR_poll */ diff --git a/usr/klibc/ppoll.c b/usr/klibc/ppoll.c new file mode 100644 index 0000000..5e20a89 --- /dev/null +++ b/usr/klibc/ppoll.c @@ -0,0 +1,19 @@ +/* + * ppoll.c + */ + +#include <sys/poll.h> +#include <sys/syscall.h> + +#ifdef __NR_ppoll + +__extern int __ppoll(struct pollfd *, nfds_t, struct timespec *, + const sigset_t *, size_t); + +int ppoll(struct pollfd *ufds, nfds_t nfds, struct timespec *timeout, + const sigset_t * sigmask) +{ + return __ppoll(ufds, nfds, timeout, sigmask, sizeof *sigmask); +} + +#endif diff --git a/usr/klibc/pread.c b/usr/klibc/pread.c new file mode 100644 index 0000000..1ac3075 --- /dev/null +++ b/usr/klibc/pread.c @@ -0,0 +1,30 @@ +/* + * pread.c + * + * Some architectures need to wrap the system call + */ + +#include <sys/types.h> +#include <endian.h> +#include <sys/syscall.h> + +#if defined(__hppa__) + +#if _BITSIZE == 32 +extern size_t __pread(int, void *, size_t, unsigned int, unsigned int); +#else +extern size_t __pread(int, void *, size_t, off_t); +#endif + +size_t pread(int fd, void *buf, size_t count, off_t offset) +{ +#if _BITSIZE == 32 + unsigned int hi = offset >> 32; + unsigned int lo = (unsigned int) offset; + return __pread(fd, buf, count, __LONG_LONG_PAIR(hi, lo)); +#else + return __pread(fd, buf, count, offset); +#endif +} + +#endif diff --git a/usr/klibc/printf.c b/usr/klibc/printf.c new file mode 100644 index 0000000..02bdba0 --- /dev/null +++ b/usr/klibc/printf.c @@ -0,0 +1,19 @@ +/* + * printf.c + */ + +#include <stdio.h> +#include <stdarg.h> + +#define BUFFER_SIZE 16384 + +int printf(const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vfprintf(stdout, format, ap); + va_end(ap); + return rv; +} diff --git a/usr/klibc/pselect.c b/usr/klibc/pselect.c new file mode 100644 index 0000000..42777f9 --- /dev/null +++ b/usr/klibc/pselect.c @@ -0,0 +1,22 @@ +/* + * pselect.c + */ + +#include <sys/select.h> +#include <sys/syscall.h> + +struct __pselect6 { + const sigset_t *sigmask; + size_t sigsize; +}; + +__extern int __pselect6(int, fd_set *, fd_set *, fd_set *, + const struct timespec *, const struct __pselect6 *); + +int pselect(int n, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, + const struct timespec *timeout, const sigset_t * sigmask) +{ + struct __pselect6 extended_sigmask = { sigmask, sizeof *sigmask }; + return __pselect6(n, readfds, writefds, exceptfds, + timeout, &extended_sigmask); +} diff --git a/usr/klibc/pty.c b/usr/klibc/pty.c new file mode 100644 index 0000000..7fcb2ba --- /dev/null +++ b/usr/klibc/pty.c @@ -0,0 +1,31 @@ +/* + * pty.c + * + * Basic Unix98 PTY functionality; assumes devpts mounted on /dev/pts + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <termios.h> +#include <sys/ioctl.h> + +char *ptsname(int fd) +{ + static char buffer[32]; /* Big enough to hold even a 64-bit pts no */ + unsigned int ptyno; + + if (ioctl(fd, TIOCGPTN, &ptyno)) + return NULL; + + snprintf(buffer, sizeof buffer, "/dev/pts/%u", ptyno); + + return buffer; +} + +int unlockpt(int fd) +{ + int unlock = 0; + + return ioctl(fd, TIOCSPTLCK, &unlock); +} diff --git a/usr/klibc/putchar.c b/usr/klibc/putchar.c new file mode 100644 index 0000000..e2224ca --- /dev/null +++ b/usr/klibc/putchar.c @@ -0,0 +1,15 @@ +/* + * putchar.c + * + * - gcc wants this + */ + +#include <stdio.h> + +#undef putchar /* Defined as a macro */ +int putchar(int); + +int putchar(int c) +{ + return fputc(c, stdout); +} diff --git a/usr/klibc/putenv.c b/usr/klibc/putenv.c new file mode 100644 index 0000000..5059ecb --- /dev/null +++ b/usr/klibc/putenv.c @@ -0,0 +1,37 @@ +/* + * putenv.c + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "env.h" + +int putenv(const char *str) +{ + char *s; + const char *e, *z; + + if (!str) { + errno = EINVAL; + return -1; + } + + e = NULL; + for (z = str; *z; z++) { + if (*z == '=') + e = z; + } + + if (!e) { + errno = EINVAL; + return -1; + } + + s = strdup(str); + if (!s) + return -1; + + return __put_env(s, e - str, 1); +} diff --git a/usr/klibc/puts.c b/usr/klibc/puts.c new file mode 100644 index 0000000..27e16e2 --- /dev/null +++ b/usr/klibc/puts.c @@ -0,0 +1,13 @@ +/* + * puts.c + */ + +#include <stdio.h> + +int puts(const char *s) +{ + if (fputs(s, stdout) < 0) + return -1; + + return _fwrite("\n", 1, stdout); +} diff --git a/usr/klibc/pwrite.c b/usr/klibc/pwrite.c new file mode 100644 index 0000000..19b219f --- /dev/null +++ b/usr/klibc/pwrite.c @@ -0,0 +1,30 @@ +/* + * pwrite.c + * + * Some architectures need to wrap the system call + */ + +#include <sys/types.h> +#include <endian.h> +#include <sys/syscall.h> + +#if defined(__hppa__) + +#if _BITSIZE == 32 +extern ssize_t __pwrite(int, const void *, size_t, unsigned int, unsigned int); +#else +extern ssize_t __pwrite(int, const void *, size_t, off_t); +#endif + +size_t pwrite(int fd, void *buf, size_t count, off_t offset) +{ +#if _BITSIZE == 32 + unsigned int hi = offset >> 32; + unsigned int lo = (unsigned int) offset; + return __pwrite(fd, buf, count, __LONG_LONG_PAIR(hi, lo)); +#else + return __pwrite(fd, buf, count, offset); +#endif +} + +#endif diff --git a/usr/klibc/qsort.c b/usr/klibc/qsort.c new file mode 100644 index 0000000..4c189fc --- /dev/null +++ b/usr/klibc/qsort.c @@ -0,0 +1,46 @@ +/* + * qsort.c + * + * This is actually combsort. It's an O(n log n) algorithm with + * simplicity/small code size being its main virtue. + */ + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +static inline size_t newgap(size_t gap) +{ + gap = (gap * 10) / 13; + if (gap == 9 || gap == 10) + gap = 11; + + if (gap < 1) + gap = 1; + return gap; +} + +void qsort(void *base, size_t nmemb, size_t size, + int (*compar) (const void *, const void *)) +{ + size_t gap = nmemb; + size_t i, j; + char *p1, *p2; + int swapped; + + if (!nmemb) + return; + + do { + gap = newgap(gap); + swapped = 0; + + for (i = 0, p1 = base; i < nmemb - gap; i++, p1 += size) { + j = i + gap; + if (compar(p1, p2 = (char *)base + j * size) > 0) { + memswap(p1, p2, size); + swapped = 1; + } + } + } while (gap > 1 || swapped); +} diff --git a/usr/klibc/raise.c b/usr/klibc/raise.c new file mode 100644 index 0000000..3b9d23d --- /dev/null +++ b/usr/klibc/raise.c @@ -0,0 +1,11 @@ +/* + * raise.c + */ + +#include <unistd.h> +#include <signal.h> + +int raise(int signal) +{ + return kill(getpid(), signal); +} diff --git a/usr/klibc/readdir.c b/usr/klibc/readdir.c new file mode 100644 index 0000000..8134c92 --- /dev/null +++ b/usr/klibc/readdir.c @@ -0,0 +1,68 @@ +/* + * readdir.c: opendir/readdir/closedir + */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> + +#define __KLIBC_DIRENT_INTERNALS +#include <dirent.h> + +DIR *fdopendir(int fd) +{ + DIR *dp = zalloc(sizeof(DIR)); + + if (!dp) + return NULL; + + dp->__fd = fd; + return dp; +} + +DIR *opendir(const char *name) +{ + int fd, err; + DIR *dp; + + fd = open(name, O_DIRECTORY | O_RDONLY); + if (fd < 0) + return NULL; + + dp = fdopendir(fd); + if (!dp) { + err = errno; + close(fd); + errno = err; + } + return dp; +} + +struct dirent *readdir(DIR *dir) +{ + struct dirent *dent; + int rv; + + if (!dir->bytes_left) { + rv = getdents(dir->__fd, dir->buffer, sizeof(dir->buffer)); + if (rv <= 0) + return NULL; + dir->bytes_left = rv; + dir->next = dir->buffer; + } + + dent = dir->next; + dir->next = (struct dirent *)((char *)dir->next + dent->d_reclen); + dir->bytes_left -= dent->d_reclen; + + return dent; +} + +int closedir(DIR *dir) +{ + int rv; + rv = close(dir->__fd); + free(dir); + return rv; +} diff --git a/usr/klibc/readlink.c b/usr/klibc/readlink.c new file mode 100644 index 0000000..0e67442 --- /dev/null +++ b/usr/klibc/readlink.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_readlink + +int readlink(const char *path, char *buf, size_t bufsiz) +{ + return readlinkat(AT_FDCWD, path, buf, bufsiz); +} + +#endif /* __NR_readlink */ diff --git a/usr/klibc/realloc.c b/usr/klibc/realloc.c new file mode 100644 index 0000000..14a2f2f --- /dev/null +++ b/usr/klibc/realloc.c @@ -0,0 +1,48 @@ +/* + * realloc.c + */ + +#include <stdlib.h> +#include <string.h> + +#include "malloc.h" + +/* FIXME: This is cheesy, it should be fixed later */ + +void *realloc(void *ptr, size_t size) +{ + struct free_arena_header *ah; + void *newptr; + size_t oldsize; + + if (!ptr) + return malloc(size); + + if (size == 0) { + free(ptr); + return NULL; + } + + /* Add the obligatory arena header, and round up */ + size = (size + 2 * sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK; + + ah = (struct free_arena_header *) + ((struct arena_header *)ptr - 1); + + if (ah->a.size >= size && size >= (ah->a.size >> 2)) { + /* This field is a good size already. */ + return ptr; + } else { + /* Make me a new block. This is kind of bogus; we should + be checking the following block to see if we can do an + in-place adjustment... fix that later. */ + + oldsize = ah->a.size - sizeof(struct arena_header); + + newptr = malloc(size); + memcpy(newptr, ptr, (size < oldsize) ? size : oldsize); + free(ptr); + + return newptr; + } +} diff --git a/usr/klibc/realpath.c b/usr/klibc/realpath.c new file mode 100644 index 0000000..1474b1e --- /dev/null +++ b/usr/klibc/realpath.c @@ -0,0 +1,49 @@ +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> + +/* + * Note that this requires name to refer to an existing file. This is + * correct according to POSIX. However, BSD and GNU implementations + * also allow name to refer to a non-existing file in an existing + * directory. + */ + +char *realpath(const char *name, char *resolved_name) +{ + static const char proc_fd_prefix[] = "/proc/self/fd/"; + char proc_fd_name[sizeof(proc_fd_prefix) + sizeof(int) * 3]; + int allocated = 0; + int fd; + ssize_t len; + + /* Open for path lookup only */ + fd = open(name, O_PATH); + if (fd < 0) + return NULL; + + if (!resolved_name) { + resolved_name = malloc(PATH_MAX); + if (!resolved_name) + goto out_close; + allocated = 1; + } + + /* Use procfs to read back the resolved name */ + sprintf(proc_fd_name, "%s%d", proc_fd_prefix, fd); + len = readlink(proc_fd_name, resolved_name, PATH_MAX - 1); + if (len < 0) { + if (allocated) + free(resolved_name); + resolved_name = NULL; + } else { + resolved_name[len] = 0; + } + +out_close: + close(fd); + return resolved_name; +} diff --git a/usr/klibc/reboot.c b/usr/klibc/reboot.c new file mode 100644 index 0000000..5aab039 --- /dev/null +++ b/usr/klibc/reboot.c @@ -0,0 +1,15 @@ +/* + * reboot.c + */ + +#include <unistd.h> +#include <sys/reboot.h> +#include <sys/syscall.h> + +/* This provides two-argument reboot function (glibc flag plus reboot argument). + The full four-argument system call is available as __reboot(). */ + +int reboot(int flag, void *arg) +{ + return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, flag, arg); +} diff --git a/usr/klibc/recv.c b/usr/klibc/recv.c new file mode 100644 index 0000000..0baa00a --- /dev/null +++ b/usr/klibc/recv.c @@ -0,0 +1,11 @@ +/* + * recv.c + */ + +#include <stddef.h> +#include <sys/socket.h> + +int recv(int s, void *buf, size_t len, unsigned int flags) +{ + return recvfrom(s, buf, len, flags, NULL, 0); +} diff --git a/usr/klibc/remove.c b/usr/klibc/remove.c new file mode 100644 index 0000000..9088204 --- /dev/null +++ b/usr/klibc/remove.c @@ -0,0 +1,18 @@ +/* + * remove.c + */ + +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +int remove(const char *pathname) +{ + int rv; + + rv = unlink(pathname); + if (rv == -1 && errno == EISDIR) + return rmdir(pathname); + + return rv; +} diff --git a/usr/klibc/rename.c b/usr/klibc/rename.c new file mode 100644 index 0000000..d76b739 --- /dev/null +++ b/usr/klibc/rename.c @@ -0,0 +1,11 @@ +#include <fcntl.h> +#include <stdio.h> + +#ifndef __NR_rename + +int rename(const char *oldpath, const char *newpath) +{ + return renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, 0); +} + +#endif /* __NR_rename */ diff --git a/usr/klibc/renameat.c b/usr/klibc/renameat.c new file mode 100644 index 0000000..10c8882 --- /dev/null +++ b/usr/klibc/renameat.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <stdio.h> + +#ifndef __NR_renameat + +int renameat(int olddirfd, const char *oldpath, + int newdirfd, const char *newpath) +{ + return renameat2(olddirfd, oldpath, newdirfd, newpath, 0); +} + +#endif /* __NR_renameat */ diff --git a/usr/klibc/rmdir.c b/usr/klibc/rmdir.c new file mode 100644 index 0000000..94ae5f2 --- /dev/null +++ b/usr/klibc/rmdir.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_rmdir + +int rmdir(const char *pathname) +{ + return unlinkat(AT_FDCWD, pathname, AT_REMOVEDIR); +} + +#endif /* __NR_rmdir */ diff --git a/usr/klibc/sbrk.c b/usr/klibc/sbrk.c new file mode 100644 index 0000000..4896ce0 --- /dev/null +++ b/usr/klibc/sbrk.c @@ -0,0 +1,45 @@ +/* sbrk.c - Change data segment size */ + +/* Written 2000 by Werner Almesberger */ +/* Modified 2003-2004 for klibc by H. Peter Anvin */ + +#include <stddef.h> +#include <unistd.h> +#include <inttypes.h> +#include <errno.h> +#include "malloc.h" + +#if !_KLIBC_NO_MMU /* uClinux doesn't have brk() */ + +char *__current_brk; /* Common with brk.c */ + +/* p is an address, a is alignment; must be a power of 2 */ +static inline void *align_up(void *p, uintptr_t a) +{ + return (void *)(((uintptr_t) p + a - 1) & ~(a - 1)); +} + +void *sbrk(ptrdiff_t increment) +{ + char *start, *end, *new_brk; + + if (!__current_brk) + __current_brk = __brk(NULL); + + start = align_up(__current_brk, _KLIBC_SBRK_ALIGNMENT); + end = start + increment; + + new_brk = __brk(end); + + if (new_brk == (void *)-1) + return (void *)-1; + else if (new_brk < end) { + errno = ENOMEM; + return (void *)-1; + } + + __current_brk = new_brk; + return start; +} + +#endif /* !_KLIBC_NO_MMU */ diff --git a/usr/klibc/scandir.c b/usr/klibc/scandir.c new file mode 100644 index 0000000..9b95980 --- /dev/null +++ b/usr/klibc/scandir.c @@ -0,0 +1,71 @@ +/* + * scandir.c: scandir + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <dirent.h> + +int scandir(const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) +{ + struct dirent **nl = NULL, **next_nl; + struct dirent *dirent; + size_t count = 0; + size_t allocated = 0; + DIR *dir; + + dir = opendir(dirp); + if (!dir) + return -1; + + while (1) { + dirent = readdir(dir); + if (!dirent) + break; + if (!filter || filter(dirent)) { + struct dirent *copy; + copy = malloc(sizeof(*copy)); + if (!copy) + goto cleanup_fail; + memcpy(copy, dirent, sizeof(*copy)); + + /* Extend the array if needed */ + if (count == allocated) { + if (allocated == 0) + allocated = 15; /* ~1 page worth */ + else + allocated *= 2; + next_nl = realloc(nl, allocated); + if (!next_nl) { + free(copy); + goto cleanup_fail; + } + nl = next_nl; + } + + nl[count++] = copy; + } + } + + qsort(nl, count, sizeof(struct dirent *), + (int (*)(const void *, const void *))compar); + + closedir(dir); + + *namelist = nl; + return count; + +cleanup_fail: + while (count) { + dirent = nl[--count]; + free(dirent); + } + free(nl); + closedir(dir); + errno = ENOMEM; + return -1; +} diff --git a/usr/klibc/seed48.c b/usr/klibc/seed48.c new file mode 100644 index 0000000..ccdf183 --- /dev/null +++ b/usr/klibc/seed48.c @@ -0,0 +1,18 @@ +/* + * seed48.c + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +unsigned short __rand48_seed[3]; + +unsigned short *seed48(const unsigned short xsubi[3]) +{ + static unsigned short oldseed[3]; + memcpy(oldseed, __rand48_seed, sizeof __rand48_seed); + memcpy(__rand48_seed, xsubi, sizeof __rand48_seed); + + return oldseed; +} diff --git a/usr/klibc/select.c b/usr/klibc/select.c new file mode 100644 index 0000000..11e7154 --- /dev/null +++ b/usr/klibc/select.c @@ -0,0 +1,31 @@ +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#include <errno.h> +#include <sys/syscall.h> + +struct __pselect6; +__extern int __pselect6(int, fd_set *, fd_set *, fd_set *, + const struct timespec *, const struct __pselect6 *); + +int select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) +{ + int result; + struct timespec ts; + + if (timeout) { + ts.tv_sec = timeout->tv_sec; + ts.tv_nsec = timeout->tv_usec * 1000; + } + + result = __pselect6(nfds, readfds, writefds, exceptfds, + timeout ? &ts : NULL, NULL); + + if (timeout) { + timeout->tv_sec = ts.tv_sec; + timeout->tv_usec = ts.tv_nsec / 1000; + } + + return result; +} diff --git a/usr/klibc/send.c b/usr/klibc/send.c new file mode 100644 index 0000000..a867dd1 --- /dev/null +++ b/usr/klibc/send.c @@ -0,0 +1,11 @@ +/* + * send.c + */ + +#include <stddef.h> +#include <sys/socket.h> + +int send(int s, const void *buf, size_t len, unsigned int flags) +{ + return sendto(s, buf, len, flags, NULL, 0); +} diff --git a/usr/klibc/setegid.c b/usr/klibc/setegid.c new file mode 100644 index 0000000..6af966f --- /dev/null +++ b/usr/klibc/setegid.c @@ -0,0 +1,10 @@ +/* + * setegid.c + */ + +#include <unistd.h> + +int setegid(gid_t egid) +{ + return setregid(-1, egid); +} diff --git a/usr/klibc/setenv.c b/usr/klibc/setenv.c new file mode 100644 index 0000000..45a7aad --- /dev/null +++ b/usr/klibc/setenv.c @@ -0,0 +1,42 @@ +/* + * setenv.c + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "env.h" + +int setenv(const char *name, const char *val, int overwrite) +{ + const char *z; + char *s; + size_t l1, l2; + + if (!name || !name[0]) { + errno = EINVAL; + return -1; + } + + l1 = 0; + for (z = name; *z; z++) { + l1++; + if (*z == '=') { + errno = EINVAL; + return -1; + } + } + + l2 = strlen(val); + + s = malloc(l1 + l2 + 2); + if (!s) + return -1; + + memcpy(s, name, l1); + s[l1] = '='; + memcpy(s + l1 + 1, val, l2 + 1); + + return __put_env(s, l1 + 1, overwrite); +} diff --git a/usr/klibc/seteuid.c b/usr/klibc/seteuid.c new file mode 100644 index 0000000..18dddb1 --- /dev/null +++ b/usr/klibc/seteuid.c @@ -0,0 +1,10 @@ +/* + * seteuid.c + */ + +#include <unistd.h> + +int seteuid(uid_t euid) +{ + return setreuid(-1, euid); +} diff --git a/usr/klibc/setmntent.c b/usr/klibc/setmntent.c new file mode 100644 index 0000000..d23e141 --- /dev/null +++ b/usr/klibc/setmntent.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <mntent.h> + +FILE *setmntent(const char *filename, const char *type) +{ + return fopen(filename, type); +} diff --git a/usr/klibc/setpgrp.c b/usr/klibc/setpgrp.c new file mode 100644 index 0000000..75bbcc7 --- /dev/null +++ b/usr/klibc/setpgrp.c @@ -0,0 +1,10 @@ +/* + * setpgrp.c + */ + +#include <unistd.h> + +int setpgrp(void) +{ + return setpgid(0, 0); +} diff --git a/usr/klibc/settimeofday.c b/usr/klibc/settimeofday.c new file mode 100644 index 0000000..75754db --- /dev/null +++ b/usr/klibc/settimeofday.c @@ -0,0 +1,22 @@ +#include <time.h> +#include <sys/time.h> +#include <sys/syscall.h> + +extern int __settimeofday(const void *, const struct timezone *); + +int settimeofday(const struct timeval *tv, const struct timezone *tz) +{ + struct timespec ts; + + if (tz && __settimeofday(NULL, tz)) + return -1; + + if (tv) { + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + if (clock_settime(CLOCK_REALTIME, &ts)) + return -1; + } + + return 0; +} diff --git a/usr/klibc/sha1hash.c b/usr/klibc/sha1hash.c new file mode 100644 index 0000000..c29eebf --- /dev/null +++ b/usr/klibc/sha1hash.c @@ -0,0 +1,317 @@ +/* +SHA-1 in C +By Steve Reid <sreid@sea-to-sky.net> +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown <jbrown@burgoyne.com> +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include <process.h> for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid <sreid@sea-to-sky.net> +Still 100% public domain + +1- Removed #include <process.h> and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz <Saul.Kravitz@celera.com> +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 2/03 +By H. Peter Anvin <hpa@zytor.com> +Still 100% PD +Modified to run on any hardware with <inttypes.h> and <netinet/in.h> +Changed the driver program + +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define SHA1HANDSOFF */ + +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <netinet/in.h> /* For htonl/ntohl/htons/ntohs */ + +/* #include <process.h> */ /* prototype for exit() - JHB */ +/* Using return() instead of exit() - SWR */ + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(uint32_t state[5], unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, unsigned char* data, uint32_t len); /* +JHB */ +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#define blk0(i) (block->l[i] = ntohl(block->l[i])) +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg){ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(uint32_t state[5], unsigned char buffer[64]) +{ +uint32_t a, b, c, d, e; +typedef union { + unsigned char c[64]; + uint32_t l[16]; +} CHAR64LONG16; +CHAR64LONG16* block; +#ifdef SHA1HANDSOFF +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, unsigned char* data, uint32_t len) /* +JHB */ +{ +uint32_t i, j; /* JHB */ + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ +uint32_t i; /* JHB */ +unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() +*/ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = 0; /* JHB */ + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} + +/*************************************************************/ + +/* This is not quite the MIME base64 algorithm: it uses _ instead of /, + and instead of padding the output with = characters we just make the + output shorter. */ +char *mybase64(uint8_t digest[20]) +{ + static const char charz[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + uint8_t input[21]; + static char output[28]; + int i, j; + uint8_t *p; + char *q; + uint32_t bv; + + memcpy(input, digest, 20); + input[20] = 0; /* Pad to multiple of 3 bytes */ + + p = input; q = output; + for ( i = 0 ; i < 7 ; i++ ) { + bv = (p[0] << 16) | (p[1] << 8) | p[2]; + p += 3; + for ( j = 0 ; j < 4 ; j++ ) { + *q++ = charz[(bv >> 18) & 0x3f]; + bv <<= 6; + } + } + *--q = '\0'; /* The last character is not significant */ + return output; +} + +int main(int argc, char** argv) +{ + int i; + SHA1_CTX context; + uint8_t digest[20], buffer[16384]; + FILE* file; + + if (argc < 2) { + file = stdin; + } + else { + if (!(file = fopen(argv[1], "rb"))) { + fputs("Unable to open file.", stderr); + return(-1); + } + } + SHA1Init(&context); + while (!feof(file)) { /* note: what if ferror(file) */ + i = fread(buffer, 1, 16384, file); + SHA1Update(&context, buffer, i); + } + SHA1Final(digest, &context); + fclose(file); + + puts(mybase64(digest)); + + return 0; +} diff --git a/usr/klibc/shared-stub.S b/usr/klibc/shared-stub.S new file mode 100644 index 0000000..15c0f02 --- /dev/null +++ b/usr/klibc/shared-stub.S @@ -0,0 +1,22 @@ +# +# This is a hack to generate the .intrp section, which then +# ld turns into an PT_INTERP header. +# +# NOTE: The .interp section needs to be "a", or it doesnt work... +# + + .section ".interp","a" + .ascii LIBDIR + .ascii "/klibc-" + .ascii SOHASH + .ascii ".so" + .byte 0 + +# +# Ensure that every executable includes some static data, as +# ELF loaders tend to assume this - see +# <https://bugs.debian.org/919921>, <https://bugs.debian.org/1016718>. +# + + .section ".data" + .byte 0 diff --git a/usr/klibc/shm_open.c b/usr/klibc/shm_open.c new file mode 100644 index 0000000..a54e246 --- /dev/null +++ b/usr/klibc/shm_open.c @@ -0,0 +1,23 @@ +/* + * shm_open.c + * + * POSIX shared memory support + */ + +#include <stdlib.h> +#include <alloca.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> + + +int shm_open(const char *path, int oflag, mode_t mode) +{ + int len = strlen(path); + char *pathbuf = alloca(len+10); + + memcpy(pathbuf, "/dev/shm/", 9); + memcpy(pathbuf+9, path, len+1); + + return open(path, oflag, mode|O_CLOEXEC); +} diff --git a/usr/klibc/shm_unlink.c b/usr/klibc/shm_unlink.c new file mode 100644 index 0000000..94a98a0 --- /dev/null +++ b/usr/klibc/shm_unlink.c @@ -0,0 +1,23 @@ +/* + * shm_unlink.c + * + * POSIX shared memory support + */ + +#include <stdlib.h> +#include <alloca.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> + + +int shm_unlink(const char *path) +{ + int len = strlen(path); + char *pathbuf = alloca(len+10); + + memcpy(pathbuf, "/dev/shm/", 9); + memcpy(pathbuf+9, path, len+1); + + return unlink(path); +} diff --git a/usr/klibc/sigabbrev.c b/usr/klibc/sigabbrev.c new file mode 100644 index 0000000..21a799c --- /dev/null +++ b/usr/klibc/sigabbrev.c @@ -0,0 +1,121 @@ +/* + * sigabbrev.h + * + * Construct the abbreviated signal list + */ + +#include <signal.h> +#include <unistd.h> + +const char *const sys_sigabbrev[NSIG] = { +#ifdef SIGABRT + [SIGABRT] = "ABRT", +#endif +#ifdef SIGALRM + [SIGALRM] = "ALRM", +#endif +#ifdef SIGBUS + [SIGBUS] = "BUS", +#endif +#ifdef SIGCHLD + [SIGCHLD] = "CHLD", +#endif +#if defined(SIGCLD) && (SIGCHLD != SIGCLD) + [SIGCLD] = "CLD", +#endif +#ifdef SIGEMT + [SIGEMT] = "EMT", +#endif +#ifdef SIGFPE + [SIGFPE] = "FPE", +#endif +#ifdef SIGHUP + [SIGHUP] = "HUP", +#endif +#ifdef SIGILL + [SIGILL] = "ILL", +#endif + /* SIGINFO == SIGPWR */ +#ifdef SIGINT + [SIGINT] = "INT", +#endif +#ifdef SIGIO + [SIGIO] = "IO", +#endif +#if defined(SIGIOT) && (SIGIOT != SIGABRT) + [SIGIOT] = "IOT", +#endif +#ifdef SIGKILL + [SIGKILL] = "KILL", +#endif +#if defined(SIGLOST) && (SIGLOST != SIGIO) && (SIGLOST != SIGPWR) + [SIGLOST] = "LOST", +#endif +#ifdef SIGPIPE + [SIGPIPE] = "PIPE", +#endif +#if defined(SIGPOLL) && (SIGPOLL != SIGIO) + [SIGPOLL] = "POLL", +#endif +#ifdef SIGPROF + [SIGPROF] = "PROF", +#endif +#ifdef SIGPWR + [SIGPWR] = "PWR", +#endif +#ifdef SIGQUIT + [SIGQUIT] = "QUIT", +#endif + /* SIGRESERVE == SIGUNUSED */ +#ifdef SIGSEGV + [SIGSEGV] = "SEGV", +#endif +#ifdef SIGSTKFLT + [SIGSTKFLT] = "STKFLT", +#endif +#ifdef SIGSTOP + [SIGSTOP] = "STOP", +#endif +#ifdef SIGSYS + [SIGSYS] = "SYS", +#endif +#ifdef SIGTERM + [SIGTERM] = "TERM", +#endif +#ifdef SIGTSTP + [SIGTSTP] = "TSTP", +#endif +#ifdef SIGTTIN + [SIGTTIN] = "TTIN", +#endif +#ifdef SIGTTOU + [SIGTTOU] = "TTOU", +#endif +#ifdef SIGURG + [SIGURG] = "URG", +#endif +#ifdef SIGUSR1 + [SIGUSR1] = "USR1", +#endif +#ifdef SIGUSR2 + [SIGUSR2] = "USR2", +#endif +#ifdef SIGVTALRM + [SIGVTALRM] = "VTALRM", +#endif +#ifdef SIGWINCH + [SIGWINCH] = "WINCH", +#endif +#ifdef SIGXCPU + [SIGXCPU] = "XCPU", +#endif +#ifdef SIGXFSZ + [SIGXFSZ] = "XFSZ", +#endif +#ifdef SIGTRAP + [SIGTRAP] = "TRAP", +#endif +#ifdef SIGCONT + [SIGCONT] = "CONT", +#endif +}; diff --git a/usr/klibc/sigaction.c b/usr/klibc/sigaction.c new file mode 100644 index 0000000..9481750 --- /dev/null +++ b/usr/klibc/sigaction.c @@ -0,0 +1,61 @@ +/* + * sigaction.c + */ + +#include <signal.h> +#include <stddef.h> +#include <sys/syscall.h> +#include <klibc/sysconfig.h> + +__extern void __sigreturn(void); + +#if _KLIBC_NEEDS_SIGACTION_FIXUP +typedef struct sigaction *act_type; +#else +typedef const struct sigaction *act_type; +#endif + +__extern int __rt_sigaction(int, act_type, struct sigaction *, size_t); + +int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) +{ + unsigned int needed_flags = 0 +#if _KLIBC_NEEDS_SA_RESTORER + | SA_RESTORER +#endif +#if _KLIBC_NEEDS_SA_SIGINFO + | SA_SIGINFO +#endif + ; + struct sigaction sa; + int rv; + + if (act && + ((act->sa_flags & needed_flags) != needed_flags || + _KLIBC_NEEDS_SIGACTION_FIXUP)) { + sa = *act; + sa.sa_flags |= needed_flags; +#if _KLIBC_NEEDS_SA_RESTORER + if (!(act->sa_flags & SA_RESTORER)) + sa.sa_restorer = &__sigreturn; +#endif + act = &sa; + } + + /* Check that we have the right signal API definitions */ + (void)sizeof(char[_NSIG >= 64 ? 1 : -1]); + (void)sizeof(char[sizeof(sigset_t) * 8 >= _NSIG ? 1 : -1]); + (void)sizeof(char[offsetof(struct sigaction, sa_mask) + + sizeof(sigset_t) == sizeof(struct sigaction) + ? 1 : -1]); + + rv = __rt_sigaction(sig, (act_type)act, oact, sizeof(sigset_t)); + +#if _KLIBC_NEEDS_SA_RESTORER + if (oact && (oact->sa_restorer == &__sigreturn)) { + oact->sa_flags &= ~SA_RESTORER; + } +#endif + + return rv; +} diff --git a/usr/klibc/siglist.c b/usr/klibc/siglist.c new file mode 100644 index 0000000..d690049 --- /dev/null +++ b/usr/klibc/siglist.c @@ -0,0 +1,121 @@ +/* + * siglist.h + * + * Construct the signal list + */ + +#include <signal.h> +#include <unistd.h> + +const char *const sys_siglist[NSIG] = { +#ifdef SIGABRT + [SIGABRT] = "Aborted", +#endif +#ifdef SIGALRM + [SIGALRM] = "Alarm clock", +#endif +#ifdef SIGBUS + [SIGBUS] = "Bus error", +#endif +#ifdef SIGCHLD + [SIGCHLD] = "Child exited", +#endif +#if defined(SIGCLD) && (SIGCHLD != SIGCLD) + [SIGCLD] = "Child exited", +#endif +#ifdef SIGEMT + [SIGEMT] = "Emulation trap", +#endif +#ifdef SIGFPE + [SIGFPE] = "Floating point exception", +#endif +#ifdef SIGHUP + [SIGHUP] = "Hangup", +#endif +#ifdef SIGILL + [SIGILL] = "Illegal instruction", +#endif + /* SIGINFO == SIGPWR */ +#ifdef SIGINT + [SIGINT] = "Interrupt", +#endif +#ifdef SIGIO + [SIGIO] = "I/O possible", +#endif +#if defined(SIGIOT) && (SIGIOT != SIGABRT) + [SIGIOT] = "I/O trap", +#endif +#ifdef SIGKILL + [SIGKILL] = "Killed", +#endif +#if defined(SIGLOST) && (SIGLOST != SIGIO) && (SIGLOST != SIGPWR) + [SIGLOST] = "Lock lost", +#endif +#ifdef SIGPIPE + [SIGPIPE] = "Broken pipe", +#endif +#if defined(SIGPOLL) && (SIGPOLL != SIGIO) + [SIGPOLL] = "Pollable event", +#endif +#ifdef SIGPROF + [SIGPROF] = "Profiling timer expired", +#endif +#ifdef SIGPWR + [SIGPWR] = "Power failure", +#endif +#ifdef SIGQUIT + [SIGQUIT] = "Quit", +#endif + /* SIGRESERVE == SIGUNUSED */ +#ifdef SIGSEGV + [SIGSEGV] = "Segment violation", +#endif +#ifdef SIGSTKFLT + [SIGSTKFLT] = "Stack fault", +#endif +#ifdef SIGSTOP + [SIGSTOP] = "Stopped (signal)", +#endif +#ifdef SIGSYS + [SIGSYS] = "Bad system call", +#endif +#ifdef SIGTERM + [SIGTERM] = "Terminated", +#endif +#ifdef SIGTSTP + [SIGTSTP] = "Stopped", +#endif +#ifdef SIGTTIN + [SIGTTIN] = "Stopped (tty input)", +#endif +#ifdef SIGTTOU + [SIGTTOU] = "Stopped (tty output)", +#endif +#ifdef SIGURG + [SIGURG] = "Urgent I/O condition", +#endif +#ifdef SIGUSR1 + [SIGUSR1] = "User signal 1", +#endif +#ifdef SIGUSR2 + [SIGUSR2] = "User signal 2", +#endif +#ifdef SIGVTALRM + [SIGVTALRM] = "Virtual timer expired", +#endif +#ifdef SIGWINCH + [SIGWINCH] = "Window size changed", +#endif +#ifdef SIGXCPU + [SIGXCPU] = "CPU time limit exceeded", +#endif +#ifdef SIGXFSZ + [SIGXFSZ] = "File size limit exceeded", +#endif +#ifdef SIGTRAP + [SIGTRAP] = "Trace/breakpoint trap", +#endif +#ifdef SIGCONT + [SIGCONT] = "Continue", +#endif +}; diff --git a/usr/klibc/siglongjmp.c b/usr/klibc/siglongjmp.c new file mode 100644 index 0000000..45f4e40 --- /dev/null +++ b/usr/klibc/siglongjmp.c @@ -0,0 +1,16 @@ +/* + * siglongjmp.c + * + * sigsetjmp() is a macro, by necessity (it's either that or write + * it in assembly), but siglongjmp() is a normal function. + */ + +#include <setjmp.h> +#include <signal.h> + +__noreturn siglongjmp(sigjmp_buf buf, int retval) +{ + if (buf->__sigs_saved) + sigprocmask(SIG_SETMASK, &buf->__sigs, NULL); + longjmp(buf->__jmpbuf, retval); +} diff --git a/usr/klibc/sigpending.c b/usr/klibc/sigpending.c new file mode 100644 index 0000000..b6c50b8 --- /dev/null +++ b/usr/klibc/sigpending.c @@ -0,0 +1,14 @@ +/* + * sigpending.c + */ + +#include <signal.h> +#include <sys/syscall.h> +#include <klibc/sysconfig.h> + +__extern int __rt_sigpending(sigset_t *, size_t); + +int sigpending(sigset_t * set) +{ + return __rt_sigpending(set, sizeof(sigset_t)); +} diff --git a/usr/klibc/sigprocmask.c b/usr/klibc/sigprocmask.c new file mode 100644 index 0000000..5a5f4ae --- /dev/null +++ b/usr/klibc/sigprocmask.c @@ -0,0 +1,14 @@ +/* + * sigprocmask.c + */ + +#include <signal.h> +#include <sys/syscall.h> +#include <klibc/sysconfig.h> + +__extern int __rt_sigprocmask(int, const sigset_t *, sigset_t *, size_t); + +int sigprocmask(int how, const sigset_t * set, sigset_t * oset) +{ + return __rt_sigprocmask(how, set, oset, sizeof(sigset_t)); +} diff --git a/usr/klibc/sigsuspend.c b/usr/klibc/sigsuspend.c new file mode 100644 index 0000000..120a00a --- /dev/null +++ b/usr/klibc/sigsuspend.c @@ -0,0 +1,15 @@ +/* + * sigsuspend.c + */ + +#include <signal.h> +#include <sys/syscall.h> +#include <klibc/sysconfig.h> +#include <klibc/havesyscall.h> + +__extern int __rt_sigsuspend(const sigset_t *, size_t); + +int sigsuspend(const sigset_t * mask) +{ + return __rt_sigsuspend(mask, sizeof *mask); +} diff --git a/usr/klibc/sleep.c b/usr/klibc/sleep.c new file mode 100644 index 0000000..47dfd29 --- /dev/null +++ b/usr/klibc/sleep.c @@ -0,0 +1,22 @@ +/* + * sleep.c + */ + +#include <errno.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +unsigned int sleep(unsigned int seconds) +{ + struct timespec ts; + + ts.tv_sec = seconds; + ts.tv_nsec = 0; + if (!nanosleep(&ts, &ts)) + return 0; + else if (errno == EINTR) + return ts.tv_sec; + else + return -1; +} diff --git a/usr/klibc/snprintf.c b/usr/klibc/snprintf.c new file mode 100644 index 0000000..713b3cd --- /dev/null +++ b/usr/klibc/snprintf.c @@ -0,0 +1,16 @@ +/* + * snprintf.c + */ + +#include <stdio.h> + +int snprintf(char *buffer, size_t n, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsnprintf(buffer, n, format, ap); + va_end(ap); + return rv; +} diff --git a/usr/klibc/socketcalls.pl b/usr/klibc/socketcalls.pl new file mode 100644 index 0000000..70ded0b --- /dev/null +++ b/usr/klibc/socketcalls.pl @@ -0,0 +1,82 @@ +#!/usr/bin/perl + +$v = $ENV{'KBUILD_VERBOSE'}; +$quiet = defined($v) ? !$v : 0; + +@args = (); +for $arg ( @ARGV ) { + if ( $arg =~ /^-/ ) { + if ( $arg eq '-q' ) { + $quiet = 1; + } else { + die "$0: Unknown option: $arg\n"; + } + } else { + push(@args, $arg); + } +} +($file, $arch, $outputdir) = @args; + +if (!open(FILE, "< $file")) { + die "$file: $!\n"; +} + +print "socketcall-objs := "; +while ( defined($line = <FILE>) ) { + chomp $line; + $line =~ s/\s*(|\#.*|\/\/.*)$//; # Strip comments and trailing blanks + next unless $line; + + if ( $line =~ /^\s*\<\?\>\s*(.*)\s+([_a-zA-Z][_a-zA-Z0-9]+)\s*\((.*)\)\s*\;$/ ) { + $type = $1; + $name = $2; + $argv = $3; + + @args = split(/\s*\,\s*/, $argv); + @cargs = (); + + $i = 0; + for $arg ( @args ) { + push(@cargs, "$arg a".$i++); + } + $nargs = $i; + print " \\\n\t${name}.o"; + + open(OUT, "> ${outputdir}/${name}.c") + or die "$0: Cannot open ${outputdir}/${name}.c\n"; + + print OUT "#include \"socketcommon.h\"\n"; + print OUT "\n"; + print OUT "#if _KLIBC_SYS_SOCKETCALL\n"; + print OUT "# define DO_THIS_SOCKETCALL\n"; + print OUT "#else\n"; + print OUT "# if !defined(__NR_${name})"; + if ($name eq 'accept') { + print OUT " && !defined(__NR_accept4)"; + } + print OUT "\n# define DO_THIS_SOCKETCALL\n"; + print OUT "# endif\n"; + print OUT "#endif\n\n"; + + print OUT "#if defined(DO_THIS_SOCKETCALL) && defined(SYS_\U${name}\E)\n\n"; + + print OUT "extern long __socketcall(int, const unsigned long *);\n\n"; + + print OUT "$type ${name}(", join(', ', @cargs), ")\n"; + print OUT "{\n"; + print OUT " unsigned long args[$nargs];\n"; + for ( $i = 0 ; $i < $nargs ; $i++ ) { + print OUT " args[$i] = (unsigned long)a$i;\n"; + } + print OUT " return ($type) __socketcall(SYS_\U${name}\E, args);\n"; + print OUT "}\n\n"; + + print OUT "#endif\n"; + + close(OUT); + } else { + die "$file:$.: Could not parse input\n"; + } +} + +print "\n"; diff --git a/usr/klibc/socketcalls/.gitignore b/usr/klibc/socketcalls/.gitignore new file mode 100644 index 0000000..67da0d1 --- /dev/null +++ b/usr/klibc/socketcalls/.gitignore @@ -0,0 +1,4 @@ +*.S +*.c +SOCKETCALLS.i +socketcalls.mk diff --git a/usr/klibc/socketcalls/Kbuild b/usr/klibc/socketcalls/Kbuild new file mode 100644 index 0000000..a974a88 --- /dev/null +++ b/usr/klibc/socketcalls/Kbuild @@ -0,0 +1,52 @@ +# +# Generate socket calls based on SOCKETCALLS.def +# + +# Include automatically generated Makefile fragment. +# It contains definition of socketcall-objs specifying name of all .o files +ifeq ($(clean),) +-include $(obj)/socketcalls.mk +endif + +# Listing of all .o files +always := klib.list + +##### +# Generate socket calls stubs +# Based on input from SOCKETCALLS.def generate socket call stubs +targets := klib.list +targets += socketcalls.mk +targets += SOCKETCALLS.i +targets += $(socketcall-objs) +clean-files += *.S *.c *.o *.list + +EXTRA_KLIBCCFLAGS := -I$(srctree)/$(src) + +quiet_cmd_makelist = LIST $@ + cmd_makelist = echo '$(filter-out FORCE,$^)' > $@ + +# Create list of all files +$(obj)/klib.list: $(call objectify,$(socketcall-objs)) FORCE + $(call if_changed,makelist) + +# Generate assembler file (.i) +# We pass -ansi to keep cpp from define e.g. "i386" as well as "__i386__" +quiet_cmd_socketcall.i = GEN $@ + cmd_socketcall.i = $(KLIBCCC) $(klibccflags) -D__ASSEMBLY__ \ + -ansi -x c -E -o $@ $< +$(obj)/SOCKETCALLS.i: $(KLIBCSRC)/SOCKETCALLS.def FORCE + $(call if_changed_dep,socketcall.i) + +# Generate socketcall stubs +quiet_cmd_socketcalls = GEN $@ + cmd_socketcalls = $(PERL) $(srctree)/$(KLIBCSRC)/socketcalls.pl \ + $(obj)/SOCKETCALLS.i \ + $(KLIBCARCH) $(obj) > $@ \ + || ( rm -f $@ ; exit 1 ) + +$(obj)/socketcalls.mk: $(srctree)/$(KLIBCSRC)/socketcalls.pl \ + $(obj)/SOCKETCALLS.i \ + $(src)/socketcommon.h + $(call cmd,socketcalls) + +PHONY += FORCE diff --git a/usr/klibc/socketcalls/socketcommon.h b/usr/klibc/socketcalls/socketcommon.h new file mode 100644 index 0000000..7fdd547 --- /dev/null +++ b/usr/klibc/socketcalls/socketcommon.h @@ -0,0 +1,15 @@ +/* + * socketcommon.h + * + * Common header file for socketcall stubs + */ + +#define __IN_SYS_COMMON +#include <errno.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <linux/net.h> diff --git a/usr/klibc/sprintf.c b/usr/klibc/sprintf.c new file mode 100644 index 0000000..c6d8758 --- /dev/null +++ b/usr/klibc/sprintf.c @@ -0,0 +1,18 @@ +/* + * sprintf.c + */ + +#include <stdio.h> +#include <unistd.h> + +int sprintf(char *buffer, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsnprintf(buffer, ~(size_t) 0, format, ap); + va_end(ap); + + return rv; +} diff --git a/usr/klibc/srand48.c b/usr/klibc/srand48.c new file mode 100644 index 0000000..e1c9567 --- /dev/null +++ b/usr/klibc/srand48.c @@ -0,0 +1,15 @@ +/* + * srand48.c + */ + +#include <stdlib.h> +#include <stdint.h> + +unsigned short __rand48_seed[3]; /* Common with mrand48.c, lrand48.c */ + +void srand48(long seedval) +{ + __rand48_seed[0] = 0x330e; + __rand48_seed[1] = (unsigned short)seedval; + __rand48_seed[2] = (unsigned short)((uint32_t) seedval >> 16); +} diff --git a/usr/klibc/sscanf.c b/usr/klibc/sscanf.c new file mode 100644 index 0000000..f53b276 --- /dev/null +++ b/usr/klibc/sscanf.c @@ -0,0 +1,17 @@ +/* + * sscanf() + */ + +#include <stdio.h> + +int sscanf(const char *str, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsscanf(str, format, ap); + va_end(ap); + + return rv; +} diff --git a/usr/klibc/stat.c b/usr/klibc/stat.c new file mode 100644 index 0000000..673fb06 --- /dev/null +++ b/usr/klibc/stat.c @@ -0,0 +1,10 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syscall.h> + +int stat(const char *path, struct stat *buf) +{ + return fstatat(AT_FDCWD, path, buf, 0); +} diff --git a/usr/klibc/statfs.c b/usr/klibc/statfs.c new file mode 100644 index 0000000..b8b8700 --- /dev/null +++ b/usr/klibc/statfs.c @@ -0,0 +1,19 @@ +/* + * statfs.c + * + * On architectures which do statfs64, wrap the system call + */ + +#include <sys/syscall.h> +#include <sys/vfs.h> + +#ifdef __NR_statfs64 + +extern int __statfs64(const char *, size_t, struct statfs *); + +int statfs(const char *path, struct statfs *buf) +{ + return __statfs64(path, sizeof *buf, buf); +} + +#endif diff --git a/usr/klibc/stdio/clearerr.c b/usr/klibc/stdio/clearerr.c new file mode 100644 index 0000000..ebdd22d --- /dev/null +++ b/usr/klibc/stdio/clearerr.c @@ -0,0 +1,9 @@ +#define __NO_STDIO_INLINES +#include "stdioint.h" + +void clearerr(FILE *__f) +{ + __f->_IO_error = 0; + __f->_IO_eof = 0; +} +__ALIAS(void, clearerr_unlocked, (FILE *), clearerr) diff --git a/usr/klibc/stdio/fclose.c b/usr/klibc/stdio/fclose.c new file mode 100644 index 0000000..756de43 --- /dev/null +++ b/usr/klibc/stdio/fclose.c @@ -0,0 +1,22 @@ +/* + * fclose.c + */ + +#include "stdioint.h" + +int fclose(FILE *file) +{ + struct _IO_file_pvt *f = stdio_pvt(file); + int rv; + + fflush(file); + + rv = close(f->pub._IO_fileno); + + /* Remove from linked list */ + f->next->prev = f->prev; + f->prev->next = f->next; + + free(f); + return rv; +} diff --git a/usr/klibc/stdio/fdopen.c b/usr/klibc/stdio/fdopen.c new file mode 100644 index 0000000..cdc35cc --- /dev/null +++ b/usr/klibc/stdio/fdopen.c @@ -0,0 +1,57 @@ +/* + * fdopen.c + * + * Common code between fopen(), fdopen() and the standard descriptors. + */ + +#include "stdioint.h" + +FILE *stdin, *stdout, *stderr; + +/* Doubly-linked list of all stdio structures */ +struct _IO_file_pvt __stdio_headnode = +{ + .prev = &__stdio_headnode, + .next = &__stdio_headnode, +}; + +FILE *fdopen(int fd, const char *mode) +{ + struct _IO_file_pvt *f; + const size_t bufoffs = + (sizeof *f + 4*sizeof(void *) - 1) & + ~(4*sizeof(void *) - 1); + + (void)mode; + + f = zalloc(bufoffs + BUFSIZ + _IO_UNGET_SLOP); + if (!f) + goto err; + + f->data = f->buf = (char *)f + bufoffs; + f->pub._IO_fileno = fd; + f->bufsiz = BUFSIZ; + f->bufmode = isatty(fd) ? _IOLBF : _IOFBF; + + /* Insert into linked list */ + f->prev = &__stdio_headnode; + f->next = __stdio_headnode.next; + f->next->prev = f; + __stdio_headnode.next = f; + + return &f->pub; + +err: + if (f) + free(f); + errno = ENOMEM; + return NULL; +} + +void __libc_init_stdio(void) +{ + stdin = fdopen(0, NULL); + stdout = fdopen(1, NULL); + stderr = fdopen(2, NULL); + stdio_pvt(stderr)->bufmode = _IONBF; +} diff --git a/usr/klibc/stdio/feof.c b/usr/klibc/stdio/feof.c new file mode 100644 index 0000000..4215c11 --- /dev/null +++ b/usr/klibc/stdio/feof.c @@ -0,0 +1,8 @@ +#define __NO_STDIO_INLINES +#include "stdioint.h" + +int feof(FILE *__f) +{ + return __f->_IO_eof; +} +__ALIAS(int, feof_unlocked, (FILE *), feof) diff --git a/usr/klibc/stdio/ferror.c b/usr/klibc/stdio/ferror.c new file mode 100644 index 0000000..819743b --- /dev/null +++ b/usr/klibc/stdio/ferror.c @@ -0,0 +1,8 @@ +#define __NO_STDIO_INLINES +#include "stdioint.h" + +int ferror(FILE *__f) +{ + return __f->_IO_error; +} +__ALIAS(int, ferror_unlocked, (FILE *), ferror) diff --git a/usr/klibc/stdio/fflush.c b/usr/klibc/stdio/fflush.c new file mode 100644 index 0000000..6ac5cf5 --- /dev/null +++ b/usr/klibc/stdio/fflush.c @@ -0,0 +1,60 @@ +/* + * fflush.c + */ + +#include "stdioint.h" + +int __fflush(struct _IO_file_pvt *f) +{ + ssize_t rv; + char *p; + + /* + * Flush any unused input data. If there is input data, there + * won't be any output data. + */ + if (__unlikely(f->ibytes)) + return fseek(&f->pub, 0, SEEK_CUR); + + p = f->buf; + while (f->obytes) { + rv = write(f->pub._IO_fileno, p, f->obytes); + if (rv == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + f->pub._IO_error = true; + return EOF; + } else if (rv == 0) { + /* EOF on output? */ + f->pub._IO_eof = true; + return EOF; + } + + p += rv; + f->obytes -= rv; + } + + return 0; +} + +int fflush(FILE *file) +{ + struct _IO_file_pvt *f; + + if (__likely(file)) { + f = stdio_pvt(file); + return __fflush(f); + } else { + int err = 0; + + for (f = __stdio_headnode.next; + f != &__stdio_headnode; + f = f->next) { + if (f->obytes) + err |= __fflush(f); + } + return err; + } +} +__ALIAS(int, fflush_unlocked, (FILE *), fflush) + diff --git a/usr/klibc/stdio/fgetc.c b/usr/klibc/stdio/fgetc.c new file mode 100644 index 0000000..5d1fc06 --- /dev/null +++ b/usr/klibc/stdio/fgetc.c @@ -0,0 +1,19 @@ +/* + * fgetc.c + */ + +#include "stdioint.h" + +int fgetc(FILE *file) +{ + struct _IO_file_pvt *f = stdio_pvt(file); + unsigned char ch; + + if (__likely(f->ibytes)) { + f->ibytes--; + return (unsigned char) *f->data++; + } else { + return _fread(&ch, 1, file) == 1 ? ch : EOF; + } +} +__ALIAS(int, fgetc_unlocked, (FILE *), fgetc) diff --git a/usr/klibc/stdio/fileno.c b/usr/klibc/stdio/fileno.c new file mode 100644 index 0000000..16c15dd --- /dev/null +++ b/usr/klibc/stdio/fileno.c @@ -0,0 +1,8 @@ +#define __NO_STDIO_INLINES +#include "stdioint.h" + +int fileno(FILE *__f) +{ + return __f->_IO_fileno; +} +__ALIAS(int, fileno_unlocked, (FILE *), fileno) diff --git a/usr/klibc/stdio/fopen.c b/usr/klibc/stdio/fopen.c new file mode 100644 index 0000000..cf098df --- /dev/null +++ b/usr/klibc/stdio/fopen.c @@ -0,0 +1,59 @@ +/* + * fopen.c + */ + +#include "stdioint.h" + +static int __parse_open_mode(const char *mode) +{ + int rwflags = O_RDONLY; + int crflags = 0; + int eflags = 0; + + while (*mode) { + switch (*mode++) { + case 'r': + rwflags = O_RDONLY; + crflags = 0; + break; + case 'w': + rwflags = O_WRONLY; + crflags = O_CREAT | O_TRUNC; + break; + case 'a': + rwflags = O_WRONLY; + crflags = O_CREAT | O_APPEND; + break; + case 'e': + eflags |= O_CLOEXEC; + break; + case 'x': + eflags |= O_EXCL; + break; + case '+': + rwflags = O_RDWR; + break; + } + } + + return rwflags | crflags | eflags; +} + +FILE *fopen(const char *file, const char *mode) +{ + int flags = __parse_open_mode(mode); + int fd, err; + FILE *f; + + fd = open(file, flags, 0666); + if (fd < 0) + return NULL; + + f = fdopen(fd, mode); + if (!f) { + err = errno; + close(fd); + errno = err; + } + return f; +} diff --git a/usr/klibc/stdio/fread.c b/usr/klibc/stdio/fread.c new file mode 100644 index 0000000..b099426 --- /dev/null +++ b/usr/klibc/stdio/fread.c @@ -0,0 +1,79 @@ +/* + * fread.c + */ + +#include <stdbool.h> +#include <string.h> +#include "stdioint.h" + +size_t _fread(void *buf, size_t count, FILE *file) +{ + struct _IO_file_pvt *f = stdio_pvt(file); + size_t bytes = 0; + size_t nb; + char *p = buf; + char *rdptr; + ssize_t rv; + bool bypass; + + if (!count) + return 0; + + if (f->obytes) /* User error! */ + __fflush(f); + + while (count) { + while (f->ibytes == 0) { + /* + * The buffer is empty, we have to read + */ + bypass = (count >= f->bufsiz); + if (bypass) { + /* Large read, bypass buffer */ + rdptr = p; + nb = count; + } else { + rdptr = f->buf + _IO_UNGET_SLOP; + nb = f->bufsiz; + } + + rv = read(f->pub._IO_fileno, rdptr, nb); + if (rv == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + f->pub._IO_error = true; + return bytes; + } else if (rv == 0) { + f->pub._IO_eof = true; + return bytes; + + + } + + if (bypass) { + p += rv; + bytes += rv; + count -= rv; + } else { + f->ibytes = rv; + f->data = rdptr; + } + + if (!count) + return bytes; + } + + /* If we get here, the buffer is non-empty */ + nb = f->ibytes; + nb = (count < nb) ? count : nb; + if (nb) { + memcpy(p, f->data, nb); + p += nb; + bytes += nb; + count -= nb; + f->data += nb; + f->ibytes -= nb; + } + } + return bytes; +} diff --git a/usr/klibc/stdio/fseek.c b/usr/klibc/stdio/fseek.c new file mode 100644 index 0000000..e35f85e --- /dev/null +++ b/usr/klibc/stdio/fseek.c @@ -0,0 +1,28 @@ +/* + * fseek.c + */ + +#include "stdioint.h" + +__extern int fseek(FILE *file, off_t where, int whence) +{ + struct _IO_file_pvt *f = stdio_pvt(file); + off_t rv; + + if (f->obytes) + if (__fflush(f)) + return -1; + + if (whence == SEEK_CUR) + where -= f->ibytes; + + rv = lseek(f->pub._IO_fileno, where, whence); + if (__likely(rv >= 0)) { + f->pub._IO_eof = false; + f->ibytes = 0; + return 0; + } else { + f->pub._IO_error = true; + return -1; + } +} diff --git a/usr/klibc/stdio/ftell.c b/usr/klibc/stdio/ftell.c new file mode 100644 index 0000000..cb1202d --- /dev/null +++ b/usr/klibc/stdio/ftell.c @@ -0,0 +1,12 @@ +#include "stdioint.h" + +off_t ftell(FILE *file) +{ + struct _IO_file_pvt *f = stdio_pvt(file); + off_t pos = lseek(f->pub._IO_fileno, 0, SEEK_CUR); + + if (pos >= 0) + pos += (int)f->obytes - (int)f->ibytes; + + return pos; +} diff --git a/usr/klibc/stdio/fwrite.c b/usr/klibc/stdio/fwrite.c new file mode 100644 index 0000000..9f32ae4 --- /dev/null +++ b/usr/klibc/stdio/fwrite.c @@ -0,0 +1,97 @@ +/* + * fwrite.c + */ + +#include <string.h> +#include "stdioint.h" + +static size_t fwrite_noflush(const void *buf, size_t count, + struct _IO_file_pvt *f) +{ + size_t bytes = 0; + size_t nb; + const char *p = buf; + ssize_t rv; + + while (count) { + if (f->ibytes || f->obytes >= f->bufsiz || + (f->obytes && count >= f->bufsiz)) + if (__fflush(f)) + break; + + if (count >= f->bufsiz) { + /* + * The write is large, so bypass + * buffering entirely. + */ + rv = write(f->pub._IO_fileno, p, count); + if (rv == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + f->pub._IO_error = true; + break; + } else if (rv == 0) { + /* EOF on output? */ + f->pub._IO_eof = true; + break; + } + + p += rv; + bytes += rv; + count -= rv; + } else { + nb = f->bufsiz - f->obytes; + nb = (count < nb) ? count : nb; + if (nb) { + memcpy(f->buf+f->obytes, p, nb); + p += nb; + f->obytes += nb; + count -= nb; + bytes += nb; + } + } + } + return bytes; +} + +size_t _fwrite(const void *buf, size_t count, FILE *file) +{ + struct _IO_file_pvt *f = stdio_pvt(file); + size_t bytes = 0; + size_t pf_len, pu_len; + const char *p = buf; + const char *q; + + /* We divide the data into two chunks, flushed (pf) + and unflushed (pu) depending on buffering mode + and contents. */ + + switch (f->bufmode) { + case _IOFBF: + pf_len = 0; + break; + + case _IOLBF: + q = memrchr(p, '\n', count); + pf_len = q ? q - p + 1 : 0; + break; + + case _IONBF: + default: + pf_len = count; + break; + } + + if (pf_len) { + bytes = fwrite_noflush(p, pf_len, f); + p += bytes; + if (__fflush(f) || bytes != pf_len) + return bytes; + } + + pu_len = count - pf_len; + if (pu_len) + bytes += fwrite_noflush(p, pu_len, f); + + return bytes; +} diff --git a/usr/klibc/stdio/rewind.c b/usr/klibc/stdio/rewind.c new file mode 100644 index 0000000..fb7cd5a --- /dev/null +++ b/usr/klibc/stdio/rewind.c @@ -0,0 +1,8 @@ +#include <stdio.h> +#include <stdbool.h> + +void rewind(FILE *f) +{ + if (!fseek(f, 0, SEEK_SET)) + f->_IO_error = false; +} diff --git a/usr/klibc/stdio/stdioint.h b/usr/klibc/stdio/stdioint.h new file mode 100644 index 0000000..724c657 --- /dev/null +++ b/usr/klibc/stdio/stdioint.h @@ -0,0 +1,38 @@ +/* + * stdioint.h + * + * stdio internals + */ + +#ifndef USR_KLIBC_STDIO_STDIOINT_H +#define USR_KLIBC_STDIO_STDIOINT_H + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +/* Actual FILE structure */ +struct _IO_file_pvt { + struct _IO_file pub; /* Data exported to inlines */ + struct _IO_file_pvt *prev, *next; + char *buf; /* Buffer */ + char *data; /* Location of input data in buffer */ + unsigned int ibytes; /* Input data bytes in buffer */ + unsigned int obytes; /* Output data bytes in buffer */ + unsigned int bufsiz; /* Total size of buffer */ + enum _IO_bufmode bufmode; /* Type of buffering */ +}; + +#define stdio_pvt(x) container_of(x, struct _IO_file_pvt, pub) + +/* Assign this much extra to the input buffer in case of ungetc() */ +#define _IO_UNGET_SLOP 32 + +__extern int __fflush(struct _IO_file_pvt *); + +__extern struct _IO_file_pvt __stdio_headnode; + +#endif /* USR_KLIBC_STDIO_STDIOINT_H */ diff --git a/usr/klibc/stdio/ungetc.c b/usr/klibc/stdio/ungetc.c new file mode 100644 index 0000000..fe328ac --- /dev/null +++ b/usr/klibc/stdio/ungetc.c @@ -0,0 +1,17 @@ +/* + * ungetc.c + */ + +#include "stdioint.h" + +int ungetc(int c, FILE *file) +{ + struct _IO_file_pvt *f = stdio_pvt(file); + + if (f->obytes || f->data <= f->buf) + return EOF; + + *(--f->data) = c; + f->ibytes++; + return c; +} diff --git a/usr/klibc/strcasecmp.c b/usr/klibc/strcasecmp.c new file mode 100644 index 0000000..ee1f28b --- /dev/null +++ b/usr/klibc/strcasecmp.c @@ -0,0 +1,24 @@ +/* + * strcasecmp.c + */ + +#include <string.h> +#include <ctype.h> + +int strcasecmp(const char *s1, const char *s2) +{ + const unsigned char *c1 = (const unsigned char *)s1; + const unsigned char *c2 = (const unsigned char *)s2; + unsigned char ch; + int d = 0; + + while (1) { + /* toupper() expects an unsigned char (implicitly cast to int) + as input, and returns an int, which is exactly what we want. */ + d = toupper(ch = *c1++) - toupper(*c2++); + if (d || !ch) + break; + } + + return d; +} diff --git a/usr/klibc/strcat.c b/usr/klibc/strcat.c new file mode 100644 index 0000000..6c5b673 --- /dev/null +++ b/usr/klibc/strcat.c @@ -0,0 +1,11 @@ +/* + * strcat.c + */ + +#include <string.h> + +char *strcat(char *dst, const char *src) +{ + strcpy(strchr(dst, '\0'), src); + return dst; +} diff --git a/usr/klibc/strchr.c b/usr/klibc/strchr.c new file mode 100644 index 0000000..6040cc4 --- /dev/null +++ b/usr/klibc/strchr.c @@ -0,0 +1,19 @@ +/* + * strchr.c + */ + +#include <string.h> +#include <klibc/compiler.h> + +char *strchr(const char *s, int c) +{ + while (*s != (char)c) { + if (!*s) + return NULL; + s++; + } + + return (char *)s; +} + +__ALIAS(char *, index, (const char *, int), strchr) diff --git a/usr/klibc/strcmp.c b/usr/klibc/strcmp.c new file mode 100644 index 0000000..3ab9f5a --- /dev/null +++ b/usr/klibc/strcmp.c @@ -0,0 +1,21 @@ +/* + * strcmp.c + */ + +#include <string.h> + +int strcmp(const char *s1, const char *s2) +{ + const unsigned char *c1 = (const unsigned char *)s1; + const unsigned char *c2 = (const unsigned char *)s2; + unsigned char ch; + int d = 0; + + while (1) { + d = (int)(ch = *c1++) - (int)*c2++; + if (d || !ch) + break; + } + + return d; +} diff --git a/usr/klibc/strcpy.c b/usr/klibc/strcpy.c new file mode 100644 index 0000000..aa656cf --- /dev/null +++ b/usr/klibc/strcpy.c @@ -0,0 +1,20 @@ +/* + * strcpy.c + * + * strcpy() + */ + +#include <string.h> + +char *strcpy(char *dst, const char *src) +{ + char *q = dst; + const char *p = src; + char ch; + + do { + *q++ = ch = *p++; + } while (ch); + + return dst; +} diff --git a/usr/klibc/strcspn.c b/usr/klibc/strcspn.c new file mode 100644 index 0000000..35fd38c --- /dev/null +++ b/usr/klibc/strcspn.c @@ -0,0 +1,12 @@ +/* + * strcspn + */ + +#include <string.h> + +#include "strxspn.h" + +size_t strcspn(const char *s, const char *reject) +{ + return __strxspn(s, reject, 1); +} diff --git a/usr/klibc/strdup.c b/usr/klibc/strdup.c new file mode 100644 index 0000000..905b51d --- /dev/null +++ b/usr/klibc/strdup.c @@ -0,0 +1,17 @@ +/* + * strdup.c + */ + +#include <string.h> +#include <stdlib.h> + +char *strdup(const char *s) +{ + int l = strlen(s) + 1; + char *d = malloc(l); + + if (d) + memcpy(d, s, l); + + return d; +} diff --git a/usr/klibc/strerror.c b/usr/klibc/strerror.c new file mode 100644 index 0000000..fed0e49 --- /dev/null +++ b/usr/klibc/strerror.c @@ -0,0 +1,33 @@ +/* + * strerror.c + */ + +#include <string.h> + +char *strerror(int errnum) +{ + static char message[32] = "error "; /* enough for error 2^63-1 */ + char numbuf[32]; + char *p; + unsigned int e = (unsigned int)errnum; + +#ifdef WITH_ERRLIST + extern const int sys_nerr; + extern const char *const sys_errlist[]; + + if (e < (unsigned int)sys_nerr && sys_errlist[e]) + return (char *)sys_errlist[e]; +#endif + + p = numbuf + sizeof numbuf; + *--p = '\0'; + + do { + *--p = (e % 10) + '0'; + e /= 10; + } while (e); + + memcpy(message + 6, p, (numbuf + sizeof numbuf) - p); + + return message; +} diff --git a/usr/klibc/strlcat.c b/usr/klibc/strlcat.c new file mode 100644 index 0000000..80c3375 --- /dev/null +++ b/usr/klibc/strlcat.c @@ -0,0 +1,31 @@ +/* + * strlcat.c + */ + +#include <string.h> +#include <klibc/compiler.h> + +size_t strlcat(char *dst, const char *src, size_t size) +{ + size_t bytes = 0; + char *q = dst; + const char *p = src; + char ch; + + while (bytes < size && *q) { + q++; + bytes++; + } + if (bytes == size) + return (bytes + strlen(src)); + + while ((ch = *p++)) { + if (bytes + 1 < size) + *q++ = ch; + + bytes++; + } + + *q = '\0'; + return bytes; +} diff --git a/usr/klibc/strlcpy.c b/usr/klibc/strlcpy.c new file mode 100644 index 0000000..51c72d8 --- /dev/null +++ b/usr/klibc/strlcpy.c @@ -0,0 +1,27 @@ +/* + * strlcpy.c + */ + +#include <string.h> +#include <klibc/compiler.h> + +size_t strlcpy(char *dst, const char *src, size_t size) +{ + size_t bytes = 0; + char *q = dst; + const char *p = src; + char ch; + + while ((ch = *p++)) { + if (bytes + 1 < size) + *q++ = ch; + + bytes++; + } + + /* If size == 0 there is no space for a final null... */ + if (size) + *q = '\0'; + + return bytes; +} diff --git a/usr/klibc/strlen.c b/usr/klibc/strlen.c new file mode 100644 index 0000000..86526a5 --- /dev/null +++ b/usr/klibc/strlen.c @@ -0,0 +1,13 @@ +/* + * strlen() + */ + +#include <string.h> + +size_t strlen(const char *s) +{ + const char *ss = s; + while (*ss) + ss++; + return ss - s; +} diff --git a/usr/klibc/strncasecmp.c b/usr/klibc/strncasecmp.c new file mode 100644 index 0000000..0551935 --- /dev/null +++ b/usr/klibc/strncasecmp.c @@ -0,0 +1,24 @@ +/* + * strncasecmp.c + */ + +#include <string.h> +#include <ctype.h> + +int strncasecmp(const char *s1, const char *s2, size_t n) +{ + const unsigned char *c1 = (const unsigned char *)s1; + const unsigned char *c2 = (const unsigned char *)s2; + unsigned char ch; + int d = 0; + + while (n--) { + /* toupper() expects an unsigned char (implicitly cast to int) + as input, and returns an int, which is exactly what we want. */ + d = toupper(ch = *c1++) - toupper(*c2++); + if (d || !ch) + break; + } + + return d; +} diff --git a/usr/klibc/strncat.c b/usr/klibc/strncat.c new file mode 100644 index 0000000..beb026c --- /dev/null +++ b/usr/klibc/strncat.c @@ -0,0 +1,22 @@ +/* + * strncat.c + */ + +#include <string.h> +#include <klibc/compiler.h> + +char *strncat(char *dst, const char *src, size_t n) +{ + char *q = strchr(dst, '\0'); + const char *p = src; + char ch; + + while (n--) { + *q++ = ch = *p++; + if (!ch) + return dst; + } + *q = '\0'; + + return dst; +} diff --git a/usr/klibc/strncmp.c b/usr/klibc/strncmp.c new file mode 100644 index 0000000..5235545 --- /dev/null +++ b/usr/klibc/strncmp.c @@ -0,0 +1,21 @@ +/* + * strncmp.c + */ + +#include <string.h> + +int strncmp(const char *s1, const char *s2, size_t n) +{ + const unsigned char *c1 = (const unsigned char *)s1; + const unsigned char *c2 = (const unsigned char *)s2; + unsigned char ch; + int d = 0; + + while (n--) { + d = (int)(ch = *c1++) - (int)*c2++; + if (d || !ch) + break; + } + + return d; +} diff --git a/usr/klibc/strncpy.c b/usr/klibc/strncpy.c new file mode 100644 index 0000000..fffc118 --- /dev/null +++ b/usr/klibc/strncpy.c @@ -0,0 +1,24 @@ +/* + * strncpy.c + */ + +#include <string.h> + +char *strncpy(char *dst, const char *src, size_t n) +{ + char *q = dst; + const char *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if (!ch) + break; + } + + /* The specs say strncpy() fills the entire buffer with NUL. Sigh. */ + memset(q, 0, n); + + return dst; +} diff --git a/usr/klibc/strndup.c b/usr/klibc/strndup.c new file mode 100644 index 0000000..20eaa8b --- /dev/null +++ b/usr/klibc/strndup.c @@ -0,0 +1,18 @@ +/* + * strndup.c + */ + +#include <string.h> +#include <stdlib.h> + +char *strndup(const char *s, size_t n) +{ + size_t l = strnlen(s, n); + char *d = malloc(l + 1); + if (!d) + return NULL; + + memcpy(d, s, l); + d[l] = '\0'; + return d; +} diff --git a/usr/klibc/strnlen.c b/usr/klibc/strnlen.c new file mode 100644 index 0000000..1678f4b --- /dev/null +++ b/usr/klibc/strnlen.c @@ -0,0 +1,18 @@ +/* + * strnlen() + */ + +#include <string.h> + +size_t strnlen(const char *s, size_t maxlen) +{ + const char *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss - s; +} diff --git a/usr/klibc/strntoimax.c b/usr/klibc/strntoimax.c new file mode 100644 index 0000000..179d9e5 --- /dev/null +++ b/usr/klibc/strntoimax.c @@ -0,0 +1,13 @@ +/* + * strntoimax.c + * + * strntoimax() + */ + +#include <stddef.h> +#include <inttypes.h> + +intmax_t strntoimax(const char *nptr, char **endptr, int base, size_t n) +{ + return (intmax_t) strntoumax(nptr, endptr, base, n); +} diff --git a/usr/klibc/strntoumax.c b/usr/klibc/strntoumax.c new file mode 100644 index 0000000..56dddad --- /dev/null +++ b/usr/klibc/strntoumax.c @@ -0,0 +1,77 @@ +/* + * strntoumax.c + * + * The strntoumax() function and associated + */ + +#include <stddef.h> +#include <stdint.h> +#include <ctype.h> +#include <inttypes.h> + +static inline int digitval(int ch) +{ + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'A' && ch <= 'Z') { + return ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'z') { + return ch - 'a' + 10; + } else { + return -1; + } +} + +uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n) +{ + int minus = 0; + uintmax_t v = 0; + int d; + + while (n && isspace((unsigned char)*nptr)) { + nptr++; + n--; + } + + /* Single optional + or - */ + if (n) { + char c = *nptr; + if (c == '-' || c == '+') { + minus = (c == '-'); + nptr++; + n--; + } + } + + if (base == 0) { + if (n >= 2 && nptr[0] == '0' && + (nptr[1] == 'x' || nptr[1] == 'X')) { + n -= 2; + nptr += 2; + base = 16; + } else if (n >= 1 && nptr[0] == '0') { + n--; + nptr++; + base = 8; + } else { + base = 10; + } + } else if (base == 16) { + if (n >= 2 && nptr[0] == '0' && + (nptr[1] == 'x' || nptr[1] == 'X')) { + n -= 2; + nptr += 2; + } + } + + while (n && (d = digitval(*nptr)) >= 0 && d < base) { + v = v * base + d; + n--; + nptr++; + } + + if (endptr) + *endptr = (char *)nptr; + + return minus ? -v : v; +} diff --git a/usr/klibc/strpbrk.c b/usr/klibc/strpbrk.c new file mode 100644 index 0000000..300dbba --- /dev/null +++ b/usr/klibc/strpbrk.c @@ -0,0 +1,14 @@ +/* + * strpbrk + */ + +#include <string.h> + +#include "strxspn.h" + +char *strpbrk(const char *s, const char *accept) +{ + const char *ss = s + __strxspn(s, accept, 1); + + return *ss ? (char *)ss : NULL; +} diff --git a/usr/klibc/strrchr.c b/usr/klibc/strrchr.c new file mode 100644 index 0000000..af68ffe --- /dev/null +++ b/usr/klibc/strrchr.c @@ -0,0 +1,23 @@ +/* + * strrchr.c + */ + +#include <string.h> +#include <klibc/compiler.h> + +char *strrchr(const char *s, int c) +{ + const char *found = NULL; + + for (;;) { + if (*s == (char)c) + found = s; + if (!*s) + break; + s++; + } + + return (char *)found; +} + +__ALIAS(char *, rindex, (const char *, int), strrchr) diff --git a/usr/klibc/strsep.c b/usr/klibc/strsep.c new file mode 100644 index 0000000..44e76bd --- /dev/null +++ b/usr/klibc/strsep.c @@ -0,0 +1,21 @@ +/* + * strsep.c + */ + +#include <string.h> + +char *strsep(char **stringp, const char *delim) +{ + char *s = *stringp; + char *e; + + if (!s) + return NULL; + + e = strpbrk(s, delim); + if (e) + *e++ = '\0'; + + *stringp = e; + return s; +} diff --git a/usr/klibc/strsignal.c b/usr/klibc/strsignal.c new file mode 100644 index 0000000..e345e9c --- /dev/null +++ b/usr/klibc/strsignal.c @@ -0,0 +1,26 @@ +/* + * strsignal.c + */ + +#include <string.h> +#include <signal.h> +#include <stdio.h> + +char *strsignal(int sig) +{ + static char buf[64]; + + if ((unsigned)sig < _NSIG && sys_siglist[sig]) + return (char *)sys_siglist[sig]; + +#ifdef SIGRTMIN + if (sig >= SIGRTMIN && sig <= SIGRTMAX) { + snprintf(buf, sizeof buf, "Real-time signal %d", + sig - SIGRTMIN); + return buf; + } +#endif + + snprintf(buf, sizeof buf, "Signal %d", sig); + return buf; +} diff --git a/usr/klibc/strspn.c b/usr/klibc/strspn.c new file mode 100644 index 0000000..828fa2b --- /dev/null +++ b/usr/klibc/strspn.c @@ -0,0 +1,12 @@ +/* + * strspn + */ + +#include <string.h> + +#include "strxspn.h" + +size_t strspn(const char *s, const char *accept) +{ + return __strxspn(s, accept, 0); +} diff --git a/usr/klibc/strstr.c b/usr/klibc/strstr.c new file mode 100644 index 0000000..8850858 --- /dev/null +++ b/usr/klibc/strstr.c @@ -0,0 +1,11 @@ +/* + * strstr.c + */ + +#include <string.h> + +char *strstr(const char *haystack, const char *needle) +{ + return (char *)memmem(haystack, strlen(haystack), needle, + strlen(needle)); +} diff --git a/usr/klibc/strtoimax.c b/usr/klibc/strtoimax.c new file mode 100644 index 0000000..0cdd088 --- /dev/null +++ b/usr/klibc/strtoimax.c @@ -0,0 +1,3 @@ +#define TYPE intmax_t +#define NAME strtoimax +#include "strtox.c" diff --git a/usr/klibc/strtok.c b/usr/klibc/strtok.c new file mode 100644 index 0000000..6b169a1 --- /dev/null +++ b/usr/klibc/strtok.c @@ -0,0 +1,12 @@ +/* + * strtok.c + */ + +#include <string.h> + +char *strtok(char *s, const char *delim) +{ + static char *holder; + + return strtok_r(s, delim, &holder); +} diff --git a/usr/klibc/strtok_r.c b/usr/klibc/strtok_r.c new file mode 100644 index 0000000..695d516 --- /dev/null +++ b/usr/klibc/strtok_r.c @@ -0,0 +1,13 @@ +#include <string.h> + +char *strtok_r(char *s, const char *delim, char **holder) +{ + if (s) + *holder = s; + + do { + s = strsep(holder, delim); + } while (s && !*s); + + return s; +} diff --git a/usr/klibc/strtol.c b/usr/klibc/strtol.c new file mode 100644 index 0000000..9efc8b9 --- /dev/null +++ b/usr/klibc/strtol.c @@ -0,0 +1,3 @@ +#define TYPE signed long +#define NAME strtol +#include "strtox.c" diff --git a/usr/klibc/strtoll.c b/usr/klibc/strtoll.c new file mode 100644 index 0000000..a9428c7 --- /dev/null +++ b/usr/klibc/strtoll.c @@ -0,0 +1,3 @@ +#define TYPE signed long long +#define NAME strtoll +#include "strtox.c" diff --git a/usr/klibc/strtotimespec.c b/usr/klibc/strtotimespec.c new file mode 100644 index 0000000..b426bf8 --- /dev/null +++ b/usr/klibc/strtotimespec.c @@ -0,0 +1,5 @@ +#define NAME strtotimespec +#define TIMEX struct timespec +#define FSEC tv_nsec +#define DECIMALS 9 +#include "strtotimex.c" diff --git a/usr/klibc/strtotimeval.c b/usr/klibc/strtotimeval.c new file mode 100644 index 0000000..280d4bc --- /dev/null +++ b/usr/klibc/strtotimeval.c @@ -0,0 +1,5 @@ +#define NAME strtotimeval +#define TIMEX struct timeval +#define FSEC tv_usec +#define DECIMALS 6 +#include "strtotimex.c" diff --git a/usr/klibc/strtotimex.c b/usr/klibc/strtotimex.c new file mode 100644 index 0000000..0624082 --- /dev/null +++ b/usr/klibc/strtotimex.c @@ -0,0 +1,40 @@ +/* + * strtotimex.c + * + * Nonstandard function which takes a string and converts it to a + * struct timespec/timeval. Returns a pointer to the first non-numeric + * character in the string. + * + */ + +#include <ctype.h> +#include <inttypes.h> +#include <stdlib.h> +#include <time.h> +#include <sys/time.h> + +char *NAME(const char *str, TIMEX * ts) +{ + int n; + char *s, *s0; + __typeof__(ts->FSEC) fs; /* Fractional seconds */ + + ts->tv_sec = strntoumax(str, &s, 10, ~(size_t) 0); + fs = 0; + + if (*s == '.') { + s0 = s + 1; + + fs = strntoumax(s0, &s, 10, DECIMALS); + n = s - s0; + + while (isdigit(*s)) + s++; + + for (; n < DECIMALS; n++) + fs *= 10; + } + + ts->FSEC = fs; + return s; +} diff --git a/usr/klibc/strtoul.c b/usr/klibc/strtoul.c new file mode 100644 index 0000000..3189aaa --- /dev/null +++ b/usr/klibc/strtoul.c @@ -0,0 +1,3 @@ +#define TYPE unsigned long +#define NAME strtoul +#include "strtox.c" diff --git a/usr/klibc/strtoull.c b/usr/klibc/strtoull.c new file mode 100644 index 0000000..83c14e9 --- /dev/null +++ b/usr/klibc/strtoull.c @@ -0,0 +1,3 @@ +#define TYPE unsigned long long +#define NAME strtoull +#include "strtox.c" diff --git a/usr/klibc/strtoumax.c b/usr/klibc/strtoumax.c new file mode 100644 index 0000000..a379710 --- /dev/null +++ b/usr/klibc/strtoumax.c @@ -0,0 +1,3 @@ +#define TYPE uintmax_t +#define NAME strtoumax +#include "strtox.c" diff --git a/usr/klibc/strtox.c b/usr/klibc/strtox.c new file mode 100644 index 0000000..c22e7c7 --- /dev/null +++ b/usr/klibc/strtox.c @@ -0,0 +1,14 @@ +/* + * strtox.c + * + * strto...() functions, by macro definition + */ + +#include <stddef.h> +#include <stdlib.h> +#include <inttypes.h> + +TYPE NAME(const char *nptr, char **endptr, int base) +{ + return (TYPE) strntoumax(nptr, endptr, base, ~(size_t) 0); +} diff --git a/usr/klibc/strxspn.c b/usr/klibc/strxspn.c new file mode 100644 index 0000000..99bdbff --- /dev/null +++ b/usr/klibc/strxspn.c @@ -0,0 +1,29 @@ +/* + * strpbrk + */ + +#include <string.h> +#include <stddef.h> +#include <inttypes.h> +#include <limits.h> +#include "strxspn.h" + +size_t __strxspn(const char *s, const char *map, int parity) +{ + char matchmap[UCHAR_MAX + 1]; + size_t n = 0; + + /* Create bitmap */ + memset(matchmap, 0, sizeof matchmap); + while (*map) + matchmap[(unsigned char)*map++] = 1; + + /* Make sure the null character never matches */ + matchmap[0] = parity; + + /* Calculate span length */ + while (matchmap[(unsigned char)*s++] ^ parity) + n++; + + return n; +} diff --git a/usr/klibc/strxspn.h b/usr/klibc/strxspn.h new file mode 100644 index 0000000..ce56ff2 --- /dev/null +++ b/usr/klibc/strxspn.h @@ -0,0 +1,12 @@ +/* + * strxspn.h + */ + +#ifndef STRXSPN_H +#define STRXSPN_H + +#include <stddef.h> + +extern size_t __strxspn(const char *s, const char *map, int parity); + +#endif diff --git a/usr/klibc/symlink.c b/usr/klibc/symlink.c new file mode 100644 index 0000000..080394f --- /dev/null +++ b/usr/klibc/symlink.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_symlink + +int symlink(const char *oldpath, const char *newpath) +{ + return symlinkat(oldpath, AT_FDCWD, newpath); +} + +#endif /* __NR_symlink */ diff --git a/usr/klibc/syscalls.pl b/usr/klibc/syscalls.pl new file mode 100644 index 0000000..3ff0222 --- /dev/null +++ b/usr/klibc/syscalls.pl @@ -0,0 +1,286 @@ +#!/usr/bin/perl +# +# Script to parse the SYSCALLS file and generate appropriate +# stubs. +# +# Pass 1: generate the C array of sizes +# Pass 2: generate the syscall stubs and other output +# + +# +# Convert a string to a C array of characters, +# e.g. foo -> 'f','o','o','\0', +# +sub chararray($) { + use bytes; + + my($s) = @_; + my($i, $c); + my($a) = ''; + + for ($i = 0; $i < length($s); $i++) { + $c = substr($s, $i, 1); + if (ord($c) < 32 || ord($c) > 126) { + $a .= sprintf("0x%02x,", ord($c)); + } elsif ($c eq "\\" || $c eq "\'") { + $a .= "\'\\$c\',"; + } else { + $a .= "\'$c\',"; + } + } + + return $a; +} + +# +# This extracts an ASCIIZ string for the type and the additional +# information. This is open-coded, because unpack("Z*") apparently +# is broken in Perl 5.6.1. +# +sub get_one_type($) { + use bytes; + + my($typestr) = @_; + my $i, $c; + my $l = length($typestr); + + for ($i = 0; $i < $l-3; $i++) { + $c = substr($typestr, $i, 1); + if ($c eq "\0") { + return (substr($typestr, 0, $i), + unpack("CC", substr($typestr, $i+1, 2)), + substr($typestr, $i+3)); + } + } + + return (undef, undef, undef, undef); +} + +$v = $ENV{'KBUILD_VERBOSE'}; +$quiet = defined($v) && ($v == 0) ? 1 : undef; + +@args = (); +undef $pass; +for $arg ( @ARGV ) { + if ( $arg =~ /^-/ ) { + if ( $arg eq '-q' ) { + $quiet = 1; + } elsif ( $arg eq '-v' ) { + $quiet = 0; + } elsif ( $arg =~ /\-([0-9]+)$/ ) { + $pass = $1+0; + } else { + die "$0: Unknown option: $arg\n"; + } + } else { + push(@args, $arg); + } +} +($file, $sysstub, $arch, $bits, $unistd, $outputdir, + $havesyscall, $typesize) = @args; + +if (!$pass) { + die "$0: Need to specify pass\n"; +} + +$quiet = ($pass != 2) unless defined($quiet); + +require "$sysstub"; + +if (!open(UNISTD, "< $unistd\0")) { + die "$0: $unistd: $!\n"; +} + +while ( defined($line = <UNISTD>) ) { + chomp $line; + + if ( $line =~ /^\#\s*define\s+__NR_([A-Za-z0-9_]+)\s+(.*\S)\s*$/ ) { + $syscalls{$1} = $2; + print STDERR "SYSCALL FOUND: $1\n" unless ( $quiet ); + } +} +close(UNISTD); + +if ($pass == 2) { + use bytes; + + if (!open(TYPESIZE, "< $typesize\0")) { + die "$0: $typesize: $!\n"; + } + + binmode TYPESIZE; + + $len = -s TYPESIZE; + if (read(TYPESIZE, $typebin, $len) != $len) { + die "$0: $typesize: short read: $!\n"; + } + close(TYPESIZE); + + $ix = index($typebin, "\x7a\xc8\xdb\x4e\x97\xb4\x9c\x19"); + if ($ix < 0) { + die "$0: $typesize: magic number not found\n"; + } + + # Remove magic number and bytes before it + $typebin = substr($typebin, $ix+8); + + # Expand the types until a terminating null + %typesize = (); + while (1) { + my $n, $sz, $si; + ($n, $sz, $si, $typebin) = get_one_type($typebin); + last if (length($n) == 0); + $typesize{$n} = $sz; + $typesign{$n} = $si; + print STDERR "TYPE $n: size $sz, sign $si\n" unless ($quiet); + } +} else { + # List here any types which should be sized even if they never occur + # in any system calls at all. + %type_list = ('int' => 1, 'long' => 1, 'long long' => 1, + 'void *' => 1, + 'intptr_t' => 1, 'uintptr_t' => 1, + 'intmax_t' => 1, 'uintmax_t' => 1); +} + +if ($pass == 2) { + if (!open(HAVESYS, "> $havesyscall\0")) { + die "$0: $havesyscall: $!\n"; + } + + print HAVESYS "#ifndef _KLIBC_HAVESYSCALL_H\n"; + print HAVESYS "#define _KLIBC_HAVESYSCALL_H 1\n\n"; +} + +if (!open(FILE, "< $file\0")) { + die "$0: $file: $!\n"; +} + + +if ($pass == 2) { + print "syscall-objs := "; +} + + +while ( defined($line = <FILE>) ) { + chomp $line; + $line =~ s/\s*(|\#.*|\/\/.*)$//; # Strip comments and trailing blanks + next unless $line; + + if ( $line =~ /^\s*(\<[^\>]+\>\s+|)([A-Za-z0-9_\*\s]+)\s+([A-Za-z0-9_,]+)(|\@[A-Za-z0-9_]+)(|\:\:[A-Za-z0-9_]+)\s*\(([^\:\)]*)\)\s*\;$/ ) { + $archs = $1; + $type = $2; + $snames = $3; + $stype = $4; + $fname = $5; + $argv = $6; + + $doit = 1; + $maybe = 0; + if ( $archs ne '' ) { + die "$file:$.: Invalid architecture spec: <$archs>\n" + unless ( $archs =~ /^\<(|\?)(|\!)([^\>\!\?]*)\>/ ); + $maybe = $1 ne ''; + $not = $2 ne ''; + $list = $3; + + $doit = $not || ($list eq ''); + + @list = split(/,/, $list); + foreach $a ( @list ) { + if ( $a eq $arch || $a eq $bits ) { + $doit = !$not; + last; + } + } + } + next if ( ! $doit ); + + undef $sname; + foreach $sn ( split(/,/, $snames) ) { + if ( defined $syscalls{$sn} ) { + $sname = $sn; + last; + } + } + if ( !defined($sname) ) { + next if ( $maybe ); + die "$file:$.: Undefined system call: $snames\n"; + } + + $type =~ s/\s*$//; + $stype =~ s/^\@//; + + if ( $fname eq '' ) { + $fname = $sname; + } else { + $fname =~ s/^\:\://; + } + + $argv =~ s/^\s+//; + $argv =~ s/\s+$//; + + if ($argv eq 'void') { + @args = (); + } else { + @args = split(/\s*\,\s*/, $argv); + } + + if ($pass == 1) { + # Pass 1: Add the types to the type list + foreach $a (@args) { + $type_list{$a}++; + } + } else { + # Pass 2: make sure all types defined, and actually generate stubs + + foreach $a (@args) { + if (!defined($typesize{$a})) { + die "$0: $typesize: type name missing: $a\n"; + } + } + + print HAVESYS "#define _KLIBC_HAVE_SYSCALL_${fname} ${sname}\n"; + print " \\\n\t${fname}.o"; + make_sysstub($outputdir, $fname, $type, $sname, $stype, @args); + } + } else { + die "$file:$.: Could not parse input: \"$line\"\n"; + } +} + +if ($pass == 1) { + # Pass 1: generate typesize.c + if (!open(TYPESIZE, "> $typesize")) { + die "$0: cannot create file: $typesize: $!\n"; + } + + print TYPESIZE "#include \"syscommon.h\"\n"; + + # This compares -2 < 1 in the appropriate type, which is true for + # signed types and false for unsigned types. We use -2 and 1 since + # gcc complains about comparing unsigned types with zero, and might + # complain equally about -1 in the future. + # + # This test is valid (as in, doesn't cause the compiler to barf) + # for pointers as well as for integral types; if we ever add system + # calls which take any other kinds of types than that then this needs + # to be smarter. + print TYPESIZE "#define SIGNED(X) ((X)-2 < (X)1)\n"; + + print TYPESIZE "\n"; + print TYPESIZE "const unsigned char type_sizes[] = {\n"; + print TYPESIZE "\t0x7a,0xc8,0xdb,0x4e,0x97,0xb4,0x9c,0x19, /* magic */\n"; + foreach $t (sort(keys(%type_list))) { + print TYPESIZE "\t", chararray($t), "0, sizeof($t), SIGNED($t),\n"; + } + print TYPESIZE "\t0, 0,\n"; # End sentinel + print TYPESIZE "};\n"; + close(TYPESIZE); +} else { + # Pass 2: finalize output files + print "\n"; + + print HAVESYS "\n#endif\n"; + close(HAVESYS); +} diff --git a/usr/klibc/syscalls/.gitignore b/usr/klibc/syscalls/.gitignore new file mode 100644 index 0000000..d255e6e --- /dev/null +++ b/usr/klibc/syscalls/.gitignore @@ -0,0 +1,6 @@ +*.S +SYSCALLS.i +syscalls.mk +syscalls.nrs +typesize.bin +typesize.c diff --git a/usr/klibc/syscalls/Kbuild b/usr/klibc/syscalls/Kbuild new file mode 100644 index 0000000..3f56798 --- /dev/null +++ b/usr/klibc/syscalls/Kbuild @@ -0,0 +1,99 @@ +# +# kbuild file for generating syscall stubs +# + +# Include automatically generated Makefile fragment. +# It contains definition of syscall-objs specifying name of all .o files +ifeq ($(clean),) +-include $(obj)/syscalls.mk +endif + +# Listing of all .o files +always := klib.list + + +##### +# Generate syscalls stubs +# Based on list in SYSCALLS.def generate stubs for sys calls. Actual arch code +# is defined in an arch specific perl file +targets += syscalls.mk +targets += klib.list +targets += SYSCALLS.i syscalls.nrs +targets += typesize.c typesize.o typesize.bin +targets += $(syscall-objs) + +# Side effect of running syscalls.pl +clean-files += $(objtree)/$(KLIBCINC)/klibc/havesyscall.h +clean-files += $(KLIBCINC)/klibc/havesyscall.h +# All the syscall stubs +clean-files += *.o *.S *.c *.list *.bin + +EXTRA_KLIBCCFLAGS := -I$(srctree)/$(src) + +quiet_cmd_makelist = LIST $@ + cmd_makelist = echo '$(filter-out FORCE,$^)' > $@ + +# Create list of all files +$(obj)/klib.list: $(call objectify,$(syscall-objs)) FORCE + $(call if_changed,makelist) + +# Generate assembler file (.i) +# We pass -ansi to keep cpp from define e.g. "i386" as well as "__i386__" +quiet_cmd_syscall.i = GEN $@ + cmd_syscall.i = $(KLIBCCC) $(klibccflags) -D__ASSEMBLY__ \ + -ansi -x c -E -o $@ $< +$(obj)/SYSCALLS.i: $(KLIBCSRC)/SYSCALLS.def FORCE + $(call if_changed_dep,syscall.i) + +# Get syscalls numbers +quiet_cmd_syscall.nrs = GEN $@ + cmd_syscall.nrs = $(KLIBCCC) $(klibccflags) -Wp,-dM -x c -E -o $@ $< +$(obj)/syscalls.nrs: $(KLIBCINC)/sys/syscall.h FORCE + $(call if_changed_dep,syscall.nrs) + +# Generate typesize.c +quiet_cmd_syscalsz = GEN $@ + cmd_syscalsz = \ + mkdir -p $(KLIBCINC)/klibc/; \ + $(PERL) $(srctree)/$(KLIBCSRC)/syscalls.pl \ + -1 $(obj)/SYSCALLS.i \ + $(srctree)/$(KLIBCSRC)/arch/$(KLIBCARCHDIR)/sysstub.ph \ + $(KLIBCARCH) $(KLIBCBITSIZE) $(obj)/syscalls.nrs \ + $(obj) \ + $(KLIBCINC)/klibc/havesyscall.h \ + $(obj)/typesize.c > $@ \ + || ( rm -f $@ ; exit 1 ) + +$(obj)/typesize.c: $(srctree)/$(KLIBCSRC)/syscalls.pl $(obj)/SYSCALLS.i \ + $(srctree)/$(KLIBCSRC)/arch/$(KLIBCARCHDIR)/sysstub.ph \ + $(src)/syscommon.h $(obj)/syscalls.nrs FORCE + $(call if_changed,syscalsz) + +# Convert typesize.o to typesize.bin +quiet_cmd_mkbin = OBJCOPY $@ + cmd_mkbin = $(KLIBCOBJCOPY) -O binary --only-section .rodata $< $@ + +$(obj)/typesize.bin: $(obj)/typesize.o FORCE + $(call if_changed,mkbin) + +# Generate $(KLIBINC)/klibc/havesyscall.h + makefile fragment +# Using sysstub.pl in arch dir generate all .S files +quiet_cmd_syscalls = GEN $@ + cmd_syscalls = \ + mkdir -p $(KLIBCINC)/klibc/; \ + $(PERL) $(srctree)/$(KLIBCSRC)/syscalls.pl -2 $(obj)/SYSCALLS.i \ + $(srctree)/$(KLIBCSRC)/arch/$(KLIBCARCHDIR)/sysstub.ph \ + $(KLIBCARCH) $(KLIBCBITSIZE) $(obj)/syscalls.nrs \ + $(obj) \ + $(KLIBCINC)/klibc/havesyscall.h \ + $(obj)/typesize.bin > $@ \ + || ( rm -f $@ ; exit 1 ) + +$(obj)/syscalls.mk: $(srctree)/$(KLIBCSRC)/syscalls.pl $(obj)/SYSCALLS.i \ + $(srctree)/$(KLIBCSRC)/arch/$(KLIBCARCHDIR)/sysstub.ph \ + $(call objectify, $(syscall-objs:.o=.S)) \ + $(src)/syscommon.h $(obj)/syscalls.nrs \ + $(obj)/typesize.bin FORCE + $(call if_changed,syscalls) + +PHONY += FORCE diff --git a/usr/klibc/syscalls/syscommon.h b/usr/klibc/syscalls/syscommon.h new file mode 100644 index 0000000..78f8858 --- /dev/null +++ b/usr/klibc/syscalls/syscommon.h @@ -0,0 +1,34 @@ +/* + * syscommon.h + * + * Common header file for system call stubs + */ + +#define __IN_SYS_COMMON +#include <errno.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/syscall.h> + +#include <poll.h> +#include <sched.h> +#include <sys/capability.h> +#include <sys/dirent.h> +#include <sys/klog.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/times.h> +#include <sys/uio.h> +#include <sys/utime.h> +#include <sys/utsname.h> +#include <sys/vfs.h> +#include <sys/wait.h> +#include <unistd.h> + +#ifdef __i386__ +# include <sys/vm86.h> +#endif diff --git a/usr/klibc/sysconf/sysconf.c b/usr/klibc/sysconf/sysconf.c new file mode 100644 index 0000000..0e21beb --- /dev/null +++ b/usr/klibc/sysconf/sysconf.c @@ -0,0 +1,8 @@ +#include <sys/sysconf.h> + +#undef sysconf + +long sysconf(int val) +{ + return __sysconf_inline(val); +} diff --git a/usr/klibc/syslog.c b/usr/klibc/syslog.c new file mode 100644 index 0000000..4052eaa --- /dev/null +++ b/usr/klibc/syslog.c @@ -0,0 +1,89 @@ +/* + * syslog.c + * + * Issue syslog messages via the kernel printk queue. + */ + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <syslog.h> +#include <unistd.h> +#include <fcntl.h> + +/* Maximum size for a kernel message */ +#define BUFLEN 1024 + +/* Logging node */ +#define LOGDEV "/dev/kmsg" + +/* Max length of ID string */ +#define MAXID 31 /* MAXID+5 must be < BUFLEN */ + +int __syslog_fd = -1; +static char id[MAXID + 1]; +static int syslog_flags = 0; + +void openlog(const char *ident, int option, int facility) +{ + int fd; + + (void)option; + (void)facility; /* Unused */ + + if (__syslog_fd == -1) { + __syslog_fd = fd = open(LOGDEV, O_WRONLY); + if (fd == -1) + return; + fcntl(fd, F_SETFD, (long)FD_CLOEXEC); + } + + syslog_flags = option; + + strncpy(id, ident ? ident : "", MAXID); +} + +void vsyslog(int prio, const char *format, va_list ap) +{ + char buf[BUFLEN]; + int len; + int fd; + + if (__syslog_fd == -1) + openlog(NULL, 0, 0); + + buf[0] = '<'; + buf[1] = LOG_PRI(prio) + '0'; + buf[2] = '>'; + len = 3; + + if (syslog_flags & LOG_PID) + len += sprintf(buf + 3, "%s[%u]: ", id, getpid()); + else if (*id) + len += sprintf(buf + 3, "%s: ", id); + + len += vsnprintf(buf + len, BUFLEN - len, format, ap); + + if (len > BUFLEN - 1) + len = BUFLEN - 1; + if (buf[len - 1] != '\n') + buf[len++] = '\n'; + + fd = __syslog_fd; + if (fd == -1) + fd = 2; /* Failed to open log, write to stderr */ + + write(fd, buf, len); + + if (syslog_flags & LOG_PERROR) + _fwrite(buf + 3, len - 3, stderr); +} + +void syslog(int prio, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vsyslog(prio, format, ap); + va_end(ap); +} diff --git a/usr/klibc/system.c b/usr/klibc/system.c new file mode 100644 index 0000000..13e9fbe --- /dev/null +++ b/usr/klibc/system.c @@ -0,0 +1,60 @@ +/* + * system.c + * + * The system() function. If this turns out to actually be *used*, + * we may want to try to detect the very simple cases (no shell magic) + * and handle them internally, instead of requiring that /bin/sh be + * present. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/wait.h> + +int system(const char *string) +{ + pid_t pid; + struct sigaction ignore, old_int, old_quit; + sigset_t masked, oldmask; + static const char *argv[] = { "/bin/sh", "-c", NULL, NULL }; + int status; + + /* Block SIGCHLD and ignore SIGINT and SIGQUIT */ + /* Do this before the fork() to avoid races */ + + ignore.sa_handler = SIG_IGN; + sigemptyset(&ignore.sa_mask); + ignore.sa_flags = 0; + sigaction(SIGINT, &ignore, &old_int); + sigaction(SIGQUIT, &ignore, &old_quit); + + sigemptyset(&masked); + sigaddset(&masked, SIGCHLD); + sigprocmask(SIG_BLOCK, &masked, &oldmask); + + pid = fork(); + + if (pid < 0) + return -1; + else if (pid == 0) { + sigaction(SIGINT, &old_int, NULL); + sigaction(SIGQUIT, &old_quit, NULL); + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + argv[2] = string; + + execve(argv[0], (char *const *)argv, (char *const *)environ); + _exit(127); + } + + /* else... */ + + waitpid(pid, &status, 0); + + sigaction(SIGINT, &old_int, NULL); + sigaction(SIGQUIT, &old_quit, NULL); + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + return status; +} diff --git a/usr/klibc/sysv_signal.c b/usr/klibc/sysv_signal.c new file mode 100644 index 0000000..beee7d7 --- /dev/null +++ b/usr/klibc/sysv_signal.c @@ -0,0 +1,11 @@ +/* + * sysv_signal.c + */ + +#include <signal.h> + +__sighandler_t sysv_signal(int signum, __sighandler_t handler) +{ + /* Linux/SysV signal() semantics */ + return __signal(signum, handler, SA_RESETHAND); +} diff --git a/usr/klibc/tests/Kbuild b/usr/klibc/tests/Kbuild new file mode 100644 index 0000000..31f0308 --- /dev/null +++ b/usr/klibc/tests/Kbuild @@ -0,0 +1,69 @@ +# +# Kbuild file for all test files +# + +test-files := $(wildcard $(srctree)/$(src)/*.c) +test-files := $(notdir $(test-files)) + +# This particular file uses a bunch of formats gcc don't know of, in order +# to test the full range of our vsnprintf() function. This outputs a bunch +# of useless warnings unless we tell it not to. +KLIBCCFLAGS_testvsnp.o := -Wno-format + +# This deliberately calls malloc() with unreasonably large values. We +# can't use cc-disable-warning here as the option to *enable* this +# warning requires a value. +KLIBCCFLAGS_malloctest3.o := $(call cc-option,-Wno-alloc-size-larger-than) + +# Disable optimisation of these test cases based on compiler knowledge +# of what the functions should do. +KLIBCCFLAGS_strsearch.o := -fno-builtin-strchr -fno-builtin-strrchr \ + -fno-builtin-strspn -fno-builtin-strcspn \ + -fno-builtin-strpbrk -fno-builtin-strstr + +static-y := $(test-files:.c=) +shared-y := $(addsuffix .shared, $(static-y)) + +environ.shared-y := environ.o +fcntl.shared-y := fcntl.o +fnmatch.shared-y := fnmatch.o +getopttest.shared-y := getopttest.o +getoptlong.shared-y := getoptlong.o +getpagesize.shared-y := getpagesize.o +hello.shared-y := hello.o +idtest.shared-y := idtest.o +lseek.shared-y := lseek.o +malloctest.shared-y := malloctest.o +malloctest2.shared-y := malloctest2.o +malloctest3.shared-y := malloctest3.o +memstrtest.shared-y := memstrtest.o +microhello.shared-y := microhello.o +minihello.shared-y := minihello.o +mmaptest.shared-y := mmaptest.o +nfs_no_rpc.shared-y := nfs_no_rpc.o +opentest.shared-y := opentest.o +pipetest.shared-y := pipetest.o +rtsig.shared-y := rtsig.o +select.shared-y := select.o +setenvtest.shared-y := setenvtest.o +setjmptest.shared-y := setjmptest.o +sigint.shared-y := sigint.o +sig-nodefer.shared-y := sig-nodefer.o +socket.shared-y := socket.o +sscanf.shared-y := sscanf.o +stat.shared-y := stat.o +statfs.shared-y := statfs.o +stdio.shared-y := stdio.o +strlcpycat.shared-y := strlcpycat.o +strsearch.shared-y := strsearch.o +strtoimax.shared-y := strtoimax.o +strtotime.shared-y := strtotime.o +sysconf.shared-y := sysconf.o +testrand48.shared-y := testrand48.o +testvsnp.shared-y := testvsnp.o +vfork.shared-y := vfork.o + +# Cleaning +clean-files := $(static-y) $(shared-y) \ + $(addsuffix .g, $(static-y) $(shared-y)) \ + $(test-files:.c=.o) diff --git a/usr/klibc/tests/environ.c b/usr/klibc/tests/environ.c new file mode 100644 index 0000000..5253d51 --- /dev/null +++ b/usr/klibc/tests/environ.c @@ -0,0 +1,23 @@ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +int main(int argc, char *argv[], char *envp[]) +{ + int i; + + /* Verify envp == environ */ + printf("Verifying envp == environ... %s\n", + (envp == environ) ? "ok" : "ERROR"); + + /* Test argc/argv */ + printf("argc = %d, argv = %p\n", argc, argv); + for (i = 0; i < argc; i++) + printf("argv[%2d] = %s\n", i, argv[i]); + + /* Test environ */ + for (i = 0; envp[i]; i++) + printf("%s\n", envp[i]); + + return 0; +} diff --git a/usr/klibc/tests/fcntl.c b/usr/klibc/tests/fcntl.c new file mode 100644 index 0000000..30cc900 --- /dev/null +++ b/usr/klibc/tests/fcntl.c @@ -0,0 +1,51 @@ +/* + * Simple test of fcntl + */ + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char *argv[]) +{ + int fd = open(argv[0], O_RDONLY); + struct flock l; + long flags; + + (void)argc; + + if (fd < 0) { + perror("open"); + exit(1); + } + + /* Get the flags on this FD */ + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + perror("F_GETFL"); + exit(1); + } + + if (flags != (O_RDONLY | O_LARGEFILE)) + fprintf(stderr, "flags = %#lx\n", flags); + + /* Set a lock on this FD */ + memset(&l, 0, sizeof l); + l.l_type = F_RDLCK; + l.l_whence = SEEK_SET; + l.l_start = 123; + l.l_len = 456; + + if (fcntl(fd, F_SETLK, &l) == -1) { + perror("F_SETLK"); + exit(1); + } + + /* Eventually, fork and try to conflict with this lock... */ + + close(fd); + return 0; +} diff --git a/usr/klibc/tests/fnmatch.c b/usr/klibc/tests/fnmatch.c new file mode 100644 index 0000000..ff8d58e --- /dev/null +++ b/usr/klibc/tests/fnmatch.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdlib.h> +#include <fnmatch.h> + +int main(int argc, char *argv[]) +{ + int flags = atoi(argv[3]); + int match = fnmatch(argv[1], argv[2], flags); + + printf("\"%s\" matches \"%s\": %d\n", argv[1], argv[2], match); + + return match; +} diff --git a/usr/klibc/tests/getoptlong.c b/usr/klibc/tests/getoptlong.c new file mode 100644 index 0000000..6c019b7 --- /dev/null +++ b/usr/klibc/tests/getoptlong.c @@ -0,0 +1,57 @@ +/* + * getoptlong.c + * + * Simple test for getopt_long, set the environment variable GETOPTTEST + * to give the argument string to getopt() + */ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <getopt.h> + +static int foo = 0; + +static const struct option long_options[] = { + { "first", 1, NULL, 'f' }, + { "second", 0, NULL, 's' }, + { "third", 2, NULL, '3' }, + { "fourth", 0, NULL, 4 }, + { "set-foo", 0, &foo, 1 }, + { NULL, 0, NULL, 0 } +}; + +int main(int argc, char *const *argv) +{ + const char *parser; + const char *showchar; + char one_char[] = "\'?\'"; + char num_buf[16]; + int c; + int longindex; + + parser = getenv("GETOPTTEST"); + if (!parser) + parser = "abzf:o:"; + + do { + c = getopt_long(argc, argv, parser, long_options, &longindex); + + if (c == EOF) { + showchar = "EOF"; + } else if (c >= 32 && c <= 126) { + one_char[1] = c; + showchar = one_char; + } else { + snprintf(num_buf, sizeof num_buf, "%d", c); + showchar = num_buf; + } + + printf("c = %s, optind = %d (\"%s\"), optarg = \"%s\", " + "optopt = \'%c\', foo = %d, longindex = %d\n", + showchar, optind, argv[optind], + optarg, optopt, foo, longindex); + } while (c != -1); + + return 0; +} diff --git a/usr/klibc/tests/getopttest.c b/usr/klibc/tests/getopttest.c new file mode 100644 index 0000000..58619d1 --- /dev/null +++ b/usr/klibc/tests/getopttest.c @@ -0,0 +1,32 @@ +/* + * getopttest.c + * + * Simple test for getopt, set the environment variable GETOPTTEST + * to give the argument string to getopt() + */ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +int main(int argc, char *const *argv) +{ + const char *parser; + char showchar[] = "\'?\'"; + int c; + + parser = getenv("GETOPTTEST"); + if (!parser) + parser = "abzf:o:"; + + do { + c = getopt(argc, argv, parser); + showchar[1] = c; + printf + ("c = %s, optind = %d (%s), optarg = \"%s\", optopt = \'%c\'\n", + (c == EOF) ? "EOF" : showchar, optind, argv[optind], + optarg, optopt); + } while (c != -1); + + return 0; +} diff --git a/usr/klibc/tests/getpagesize.c b/usr/klibc/tests/getpagesize.c new file mode 100644 index 0000000..1ae946a --- /dev/null +++ b/usr/klibc/tests/getpagesize.c @@ -0,0 +1,10 @@ +#include <unistd.h> +#include <stdio.h> + +int main(void) +{ + printf("getpagesize() = %d\n" + "__getpageshift() = %d\n", getpagesize(), __getpageshift()); + + return 0; +} diff --git a/usr/klibc/tests/hello.c b/usr/klibc/tests/hello.c new file mode 100644 index 0000000..3f859bd --- /dev/null +++ b/usr/klibc/tests/hello.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(void) +{ + printf("Hello, World!\n"); + return 0; +} diff --git a/usr/klibc/tests/idtest.c b/usr/klibc/tests/idtest.c new file mode 100644 index 0000000..c3c4447 --- /dev/null +++ b/usr/klibc/tests/idtest.c @@ -0,0 +1,14 @@ +#include <stdio.h> +#include <unistd.h> + +int main(void) +{ + printf("pid = %u\n", getpid()); + printf("ppid = %u\n", getppid()); + printf("uid = %u\n", getuid()); + printf("euid = %u\n", geteuid()); + printf("gid = %u\n", getgid()); + printf("egid = %u\n", getegid()); + sleep(10); + return 0; +} diff --git a/usr/klibc/tests/lseek.c b/usr/klibc/tests/lseek.c new file mode 100644 index 0000000..c4e7f54 --- /dev/null +++ b/usr/klibc/tests/lseek.c @@ -0,0 +1,15 @@ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +int main(void) +{ + int fd = open("test.out", O_RDWR|O_CREAT|O_TRUNC, 0666); + off_t rv; + + rv = lseek(fd, 123456789012ULL, SEEK_SET); + + printf("seek to: %lld\n", rv); + return 0; +} diff --git a/usr/klibc/tests/malloctest.c b/usr/klibc/tests/malloctest.c new file mode 100644 index 0000000..2ae184e --- /dev/null +++ b/usr/klibc/tests/malloctest.c @@ -0,0 +1,4146 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define NCYCLES 4096 + +int sizes[NCYCLES] = { + 11986, + 277806, + 2659, + 46, + 0, + 775553, + 1991, + 21, + 7638, + 250197, + 155828, + 5777, + 9, + 315006, + 900788, + 0, + 24893, + 119996, + 72299, + 171266, + 357, + 560, + 368, + 22952, + 54058, + 12638, + 39155, + 2738, + 217563, + 26853, + 47, + 75, + 1167, + 16917, + 1899, + 2905, + 9337, + 62243, + 14214, + 270523, + 4024, + 21, + 32, + 14892, + 625144, + 13, + 21700, + 8804, + 254147, + 0, + 6, + 836004, + 1718, + 2289, + 15554, + 412857, + 185097, + 806709, + 64, + 18602, + 17064, + 1779, + 78153, + 170600, + 199100, + 546528, + 0, + 21, + 20609, + 16514, + 548196, + 311446, + 53484, + 0, + 551, + 22225, + 24, + 153989, + 457309, + 526833, + 227979, + 757167, + 429560, + 0, + 835, + 1702, + 475275, + 798416, + 753, + 0, + 11126, + 145779, + 2006, + 0, + 8182, + 0, + 569432, + 9671, + 36, + 5523, + 407325, + 0, + 65, + 9293, + 0, + 6793, + 468701, + 73, + 0, + 186236, + 0, + 328405, + 125616, + 508013, + 380519, + 599518, + 83, + 151973, + 466906, + 9029, + 159725, + 1316, + 1, + 911532, + 1508, + 19050, + 972850, + 126, + 439377, + 29, + 37928, + 149628, + 54, + 130248, + 2, + 143, + 0, + 716873, + 3327, + 5, + 116131, + 5124, + 559621, + 2886, + 534, + 186432, + 441, + 7348, + 10331, + 1, + 260935, + 7, + 4370, + 405415, + 2, + 84518, + 1970, + 1, + 281910, + 46, + 274, + 2273, + 370565, + 4190, + 820641, + 577970, + 32809, + 974893, + 398067, + 380698, + 4, + 25978, + 153, + 882668, + 312365, + 9523, + 156421, + 0, + 268143, + 6, + 2, + 42987, + 212, + 12303, + 6723, + 1179, + 0, + 120924, + 3877, + 330421, + 310445, + 39264, + 8, + 85380, + 464716, + 0, + 33657, + 6285, + 0, + 4491, + 229, + 50, + 373197, + 6029, + 19, + 86884, + 243745, + 335656, + 90945, + 38973, + 572950, + 164129, + 0, + 3, + 17, + 13579, + 4448, + 47, + 3, + 132966, + 726249, + 498503, + 256, + 0, + 25841, + 0, + 7, + 945380, + 11872, + 69, + 3799, + 77223, + 1914, + 73, + 810968, + 10223, + 257918, + 184252, + 350, + 8101, + 725, + 9, + 2, + 2089, + 175, + 247, + 185964, + 36517, + 3723, + 313465, + 209, + 1300, + 128071, + 7425, + 2436, + 62, + 13753, + 9514, + 41, + 409141, + 46643, + 20866, + 15664, + 388548, + 84692, + 9549, + 610, + 7213, + 14, + 14930, + 244719, + 4748, + 41682, + 401098, + 102506, + 176535, + 0, + 5133, + 548, + 5234, + 56, + 11101, + 87638, + 336579, + 291705, + 640250, + 768165, + 370, + 2809, + 3, + 0, + 445122, + 47190, + 24885, + 143556, + 84, + 504726, + 610020, + 40355, + 902230, + 4360, + 1747, + 3496, + 489501, + 19, + 801601, + 62189, + 48, + 2645, + 320601, + 27304, + 17740, + 344, + 10, + 991, + 925503, + 0, + 315, + 251, + 3611, + 1756, + 683, + 165, + 380132, + 181101, + 453041, + 892056, + 67191, + 252592, + 32407, + 56242, + 8, + 297173, + 542903, + 830334, + 585236, + 422555, + 44769, + 0, + 68, + 4143, + 38754, + 73539, + 44579, + 94001, + 428537, + 38554, + 106612, + 0, + 182987, + 831731, + 3605, + 752851, + 52, + 72, + 120872, + 963754, + 31, + 764, + 240592, + 99101, + 328538, + 440325, + 12211, + 151282, + 353436, + 2991, + 40710, + 5212, + 5106, + 139122, + 148915, + 498505, + 1366, + 516, + 29190, + 17, + 224208, + 40, + 89, + 19190, + 8, + 25377, + 10029, + 720, + 97963, + 0, + 614, + 244567, + 2113, + 903675, + 8388, + 6, + 390705, + 325006, + 284272, + 108086, + 17, + 2628, + 952530, + 20474, + 898276, + 138661, + 3883, + 903, + 569993, + 376918, + 5849, + 103404, + 794499, + 35388, + 5, + 0, + 961626, + 27415, + 1927, + 92036, + 46241, + 35978, + 7426, + 399884, + 29490, + 252655, + 675971, + 3509, + 54170, + 170790, + 831341, + 134579, + 0, + 790422, + 35, + 930830, + 97394, + 20265, + 670, + 38497, + 1759, + 71209, + 93, + 736, + 11, + 886, + 1961, + 7, + 210607, + 62226, + 186736, + 1518, + 5, + 5, + 13, + 66989, + 442321, + 0, + 607939, + 11253, + 210875, + 495530, + 2, + 221136, + 377663, + 372, + 200658, + 18591, + 129783, + 803411, + 867506, + 757446, + 48836, + 34, + 200, + 114983, + 7287, + 22849, + 226669, + 13, + 0, + 20164, + 7828, + 39, + 49448, + 26740, + 185566, + 9927, + 36192, + 91068, + 338368, + 926, + 27746, + 534794, + 936132, + 2922, + 5, + 183162, + 256846, + 242551, + 134318, + 212959, + 167162, + 470, + 477045, + 532116, + 483794, + 733, + 5335, + 83074, + 4686, + 9567, + 1, + 195100, + 40354, + 87338, + 369, + 800, + 0, + 194504, + 469051, + 363532, + 850574, + 5085, + 167027, + 794511, + 124320, + 303231, + 132195, + 13225, + 46333, + 4313, + 89, + 799, + 51482, + 0, + 26, + 12659, + 1045, + 23621, + 0, + 74926, + 490979, + 6, + 3188, + 9448, + 174730, + 38982, + 102317, + 189621, + 853, + 29227, + 43374, + 423, + 420951, + 686, + 128, + 31291, + 0, + 402819, + 663143, + 55903, + 142, + 2, + 331584, + 197164, + 7, + 671983, + 53, + 5020, + 9782, + 123, + 743407, + 1276, + 1115, + 1169, + 122752, + 824690, + 292030, + 2094, + 144626, + 0, + 297278, + 440, + 742, + 95879, + 17682, + 10654, + 31, + 22183, + 746, + 0, + 0, + 11185, + 28, + 394987, + 36, + 474, + 243749, + 1431, + 56702, + 76, + 15619, + 33071, + 12181, + 158647, + 261786, + 1, + 119783, + 48816, + 6278, + 4121, + 61122, + 69, + 48790, + 345335, + 275917, + 964393, + 424, + 586433, + 20519, + 18156, + 756400, + 27736, + 458706, + 1, + 3286, + 929624, + 1883, + 2, + 1086, + 439501, + 552, + 157132, + 5565, + 105061, + 8199, + 23, + 178797, + 0, + 130644, + 1, + 6952, + 754, + 500, + 647683, + 0, + 959079, + 622561, + 1131, + 559783, + 6862, + 175420, + 408671, + 463461, + 55908, + 606496, + 169, + 49060, + 247, + 953, + 333030, + 0, + 23399, + 29193, + 9303, + 15, + 515402, + 34961, + 365856, + 633043, + 173, + 556089, + 1809, + 12215, + 14, + 316, + 20642, + 9, + 15, + 190391, + 951463, + 25059, + 13654, + 385040, + 4272, + 929033, + 208813, + 35166, + 42849, + 662648, + 254811, + 4230, + 812459, + 681, + 390168, + 5381, + 4662, + 173257, + 478863, + 103, + 89332, + 0, + 0, + 589484, + 19369, + 94, + 9, + 639917, + 1110, + 393, + 101040, + 911, + 152899, + 0, + 2, + 0, + 0, + 335691, + 43694, + 62273, + 200121, + 2250, + 621004, + 149918, + 41063, + 218229, + 0, + 497924, + 16832, + 587071, + 0, + 0, + 729918, + 2, + 808513, + 9417, + 718, + 0, + 2769, + 28704, + 1335, + 734726, + 219157, + 786230, + 981004, + 350788, + 884529, + 0, + 87872, + 34647, + 85469, + 4524, + 339838, + 38228, + 0, + 4151, + 1145, + 0, + 351, + 167956, + 810075, + 689, + 251212, + 583068, + 2929, + 189456, + 2089, + 48749, + 278952, + 77134, + 0, + 0, + 45595, + 281829, + 969602, + 43999, + 69824, + 856982, + 61732, + 336, + 25488, + 213, + 46683, + 1909, + 174097, + 57930, + 91466, + 828418, + 95740, + 378828, + 128065, + 68068, + 0, + 13312, + 26006, + 6760, + 51, + 276081, + 640068, + 634985, + 7131, + 784882, + 790126, + 628585, + 205824, + 764965, + 17793, + 3159, + 649924, + 0, + 37383, + 9919, + 353, + 0, + 149003, + 620629, + 95928, + 2560, + 504343, + 1000, + 32, + 43836, + 407031, + 207, + 800894, + 3222, + 51028, + 7, + 6, + 22010, + 0, + 21174, + 12893, + 824932, + 7305, + 70, + 624258, + 372139, + 21504, + 387996, + 418931, + 914268, + 576, + 0, + 0, + 618224, + 787516, + 133014, + 422, + 383124, + 656318, + 4420, + 6082, + 244813, + 38585, + 3200, + 1, + 2, + 11882, + 113, + 45581, + 13121, + 95475, + 807219, + 8195, + 995116, + 13, + 2146, + 369925, + 60103, + 25, + 125165, + 51300, + 4894, + 173261, + 74186, + 1044, + 122992, + 1243, + 21703, + 26294, + 197, + 333825, + 426872, + 719580, + 3598, + 106, + 0, + 9932, + 61509, + 146, + 721428, + 964781, + 319850, + 573802, + 7458, + 317889, + 0, + 133086, + 87836, + 60496, + 304249, + 1565, + 27, + 42, + 899324, + 189637, + 8648, + 104570, + 901598, + 447765, + 24, + 108, + 120127, + 828626, + 8, + 899514, + 28, + 13, + 7576, + 163390, + 1625, + 3023, + 155175, + 2, + 391, + 1, + 493073, + 398, + 210771, + 26266, + 287999, + 38255, + 249666, + 598202, + 119601, + 216933, + 91205, + 0, + 7247, + 77077, + 565383, + 29102, + 253641, + 48855, + 19722, + 463536, + 40182, + 65393, + 829444, + 598402, + 1590, + 798, + 467, + 834847, + 3007, + 13711, + 0, + 195, + 101662, + 255749, + 129201, + 11965, + 1781, + 13349, + 3100, + 718066, + 99, + 712450, + 888215, + 42503, + 43171, + 494946, + 0, + 2175, + 12387, + 25662, + 78, + 739030, + 0, + 19, + 427526, + 4275, + 5583, + 0, + 2447, + 132398, + 26437, + 3873, + 440035, + 21, + 6, + 35432, + 41523, + 7179, + 712703, + 428868, + 2793, + 6, + 286277, + 1882, + 95116, + 2959, + 86, + 115425, + 81386, + 59836, + 37, + 247598, + 34732, + 249, + 500110, + 5589, + 40319, + 575, + 12145, + 385829, + 565600, + 582150, + 92, + 223209, + 0, + 910, + 1048, + 47329, + 90944, + 235, + 8739, + 686685, + 1753, + 126, + 434, + 609477, + 25021, + 6610, + 52675, + 4, + 717846, + 150864, + 418583, + 17751, + 513794, + 181362, + 329556, + 10426, + 717019, + 457, + 616, + 388984, + 17, + 8338, + 59531, + 32, + 99565, + 376146, + 134578, + 966, + 0, + 0, + 174, + 2105, + 555, + 8990, + 298, + 169932, + 247281, + 240918, + 298655, + 158743, + 15994, + 95708, + 51, + 2985, + 4294, + 731934, + 185640, + 1483, + 87, + 742033, + 9, + 1345, + 3680, + 133530, + 9355, + 800111, + 28508, + 0, + 369, + 31681, + 24, + 8237, + 313380, + 4732, + 275423, + 951592, + 0, + 41381, + 225515, + 393004, + 526, + 187, + 19515, + 6006, + 28923, + 310151, + 2390, + 374, + 0, + 19142, + 72, + 114, + 193305, + 24035, + 397067, + 18, + 14839, + 3473, + 164, + 104622, + 378958, + 2218, + 0, + 89053, + 105183, + 312265, + 82146, + 147210, + 3419, + 5178, + 34948, + 46836, + 41319, + 842825, + 595972, + 0, + 249625, + 325, + 608, + 372328, + 119634, + 7504, + 920214, + 7302, + 444532, + 359213, + 27265, + 1755, + 48, + 126799, + 651270, + 818220, + 799493, + 724024, + 64047, + 73699, + 206999, + 209, + 1581, + 0, + 42937, + 301144, + 73416, + 0, + 242058, + 29660, + 3, + 34709, + 162719, + 2863, + 3992, + 5212, + 151814, + 3092, + 198001, + 44331, + 36, + 407, + 364771, + 1349, + 502772, + 214726, + 607, + 388583, + 137660, + 337124, + 13279, + 10549, + 943075, + 164068, + 19157, + 38443, + 26351, + 0, + 67167, + 735, + 46486, + 130305, + 232330, + 744, + 882337, + 2, + 69275, + 126354, + 9370, + 2845, + 299, + 38988, + 37834, + 0, + 306433, + 9139, + 237132, + 0, + 500, + 13462, + 373684, + 107453, + 381924, + 347915, + 4329, + 1668, + 3960, + 370661, + 3614, + 636048, + 0, + 487449, + 64925, + 333894, + 11, + 52192, + 531200, + 155554, + 461, + 1547, + 994361, + 11955, + 321056, + 37425, + 14249, + 69151, + 621862, + 174, + 79607, + 34, + 77577, + 13723, + 267550, + 13801, + 698, + 12, + 171556, + 57354, + 676845, + 0, + 24965, + 908955, + 570483, + 0, + 296387, + 983966, + 85012, + 130298, + 151946, + 384474, + 731455, + 150699, + 772, + 216131, + 346, + 130935, + 3472, + 18, + 426045, + 677262, + 808, + 17030, + 5188, + 0, + 491153, + 67299, + 19, + 60342, + 69, + 0, + 76478, + 95763, + 0, + 28778, + 147869, + 335927, + 27846, + 2163, + 22750, + 162, + 23, + 11391, + 469099, + 5852, + 63, + 0, + 0, + 22193, + 165, + 489007, + 9249, + 12477, + 2841, + 223532, + 13877, + 173, + 3570, + 45477, + 233073, + 23296, + 64377, + 4910, + 8, + 76246, + 411147, + 287411, + 10450, + 3667, + 1, + 500933, + 31363, + 257, + 1705, + 6036, + 49934, + 13738, + 13485, + 61608, + 561978, + 76493, + 16377, + 1817, + 0, + 235600, + 0, + 16347, + 680478, + 5115, + 895607, + 138270, + 369912, + 53110, + 0, + 647083, + 85, + 458681, + 163227, + 52767, + 196, + 267719, + 14047, + 147293, + 814457, + 174896, + 0, + 34138, + 36, + 21575, + 3, + 0, + 0, + 38391, + 2597, + 2, + 1433, + 3807, + 36476, + 287, + 141530, + 29389, + 495655, + 30014, + 0, + 550766, + 11958, + 348, + 226760, + 15, + 251353, + 675788, + 518308, + 215, + 81987, + 409862, + 559596, + 114283, + 4925, + 0, + 17, + 14221, + 0, + 162, + 766370, + 4898, + 998, + 493, + 138418, + 265159, + 12152, + 5229, + 1204, + 1814, + 432530, + 2889, + 144, + 1149, + 35886, + 636931, + 6640, + 1508, + 414118, + 858, + 20039, + 17398, + 3, + 5094, + 6, + 13996, + 6754, + 362, + 451487, + 11471, + 7896, + 330009, + 244269, + 99928, + 0, + 14311, + 9949, + 15251, + 283923, + 123754, + 188360, + 93902, + 854384, + 548001, + 531788, + 26298, + 328479, + 941, + 246535, + 106320, + 28769, + 440, + 4, + 61262, + 55615, + 170, + 989327, + 692534, + 8063, + 445842, + 4434, + 255349, + 117781, + 6, + 9249, + 136216, + 38165, + 307012, + 12, + 2341, + 18062, + 371882, + 662154, + 12623, + 176847, + 332220, + 590935, + 33682, + 0, + 121374, + 67, + 46841, + 495890, + 640, + 19, + 14737, + 11032, + 17, + 5993, + 302562, + 827710, + 165346, + 49607, + 87863, + 308513, + 735300, + 1914, + 2900, + 207308, + 9068, + 83494, + 179, + 417, + 41605, + 74681, + 652171, + 4013, + 29811, + 13966, + 8136, + 78, + 61182, + 674187, + 0, + 331121, + 0, + 18559, + 386, + 77, + 348439, + 975358, + 18, + 33700, + 47396, + 204751, + 2350, + 26503, + 0, + 83653, + 446, + 10844, + 485, + 9241, + 88347, + 232419, + 936900, + 43250, + 2, + 26112, + 811955, + 20723, + 102069, + 42255, + 8431, + 119508, + 4080, + 13565, + 12, + 46110, + 62096, + 638777, + 44025, + 152985, + 13362, + 3, + 12331, + 193337, + 56419, + 14593, + 3837, + 282314, + 403454, + 48589, + 135, + 18350, + 2160, + 90, + 918216, + 7083, + 105534, + 742826, + 399028, + 1470, + 23770, + 480, + 677884, + 340472, + 107406, + 0, + 5002, + 445, + 748948, + 534012, + 592464, + 6539, + 819632, + 3138, + 4, + 39397, + 229683, + 12204, + 2439, + 65131, + 817226, + 22596, + 0, + 1046, + 94638, + 0, + 95403, + 1230, + 790056, + 19976, + 43085, + 14251, + 139187, + 20232, + 693, + 3058, + 27654, + 65690, + 40948, + 15001, + 21089, + 14425, + 322459, + 13571, + 228154, + 536814, + 761221, + 28030, + 2322, + 921, + 1, + 1137, + 187815, + 8, + 34911, + 4527, + 15, + 46, + 78801, + 0, + 73605, + 44, + 28233, + 1370, + 73409, + 198159, + 66586, + 3, + 2576, + 15, + 35460, + 263237, + 44997, + 2873, + 240, + 1781, + 269, + 46, + 272778, + 28404, + 8232, + 417073, + 234591, + 9, + 720349, + 1176, + 16195, + 0, + 9705, + 0, + 14, + 947048, + 163, + 76288, + 1115, + 267020, + 3416, + 414217, + 441004, + 95131, + 765002, + 6196, + 9069, + 27017, + 137039, + 65247, + 266489, + 484945, + 187008, + 45405, + 5700, + 9, + 7751, + 12, + 294, + 3093, + 6350, + 103303, + 6045, + 252345, + 140207, + 22390, + 234867, + 443326, + 1, + 0, + 89972, + 8637, + 427150, + 22146, + 0, + 310432, + 390333, + 10461, + 1632, + 31403, + 908653, + 0, + 6543, + 163479, + 67608, + 195543, + 315889, + 822964, + 383536, + 954954, + 1619, + 241, + 96053, + 104556, + 767302, + 2469, + 12, + 164330, + 78, + 141, + 170519, + 268214, + 53338, + 48342, + 721, + 58980, + 4345, + 1, + 856265, + 87289, + 57219, + 775679, + 123992, + 695804, + 113025, + 832, + 117420, + 16634, + 352, + 24729, + 14973, + 25622, + 131290, + 0, + 22, + 87740, + 5917, + 533, + 2934, + 34261, + 9174, + 0, + 1656, + 764587, + 54652, + 35597, + 36389, + 577889, + 63957, + 26808, + 34556, + 56, + 15641, + 137, + 1, + 3, + 11724, + 197397, + 39027, + 87902, + 320, + 791479, + 7, + 487864, + 0, + 433, + 25733, + 6956, + 15407, + 312557, + 526302, + 383019, + 340215, + 96, + 276158, + 6493, + 135613, + 2000, + 1218, + 930, + 276808, + 273249, + 8896, + 397, + 735095, + 20648, + 2079, + 5349, + 205, + 356313, + 841954, + 8255, + 266874, + 0, + 965, + 287993, + 1549, + 207833, + 75, + 178180, + 39072, + 0, + 43254, + 3847, + 227, + 2712, + 161043, + 463264, + 74720, + 795789, + 12, + 6812, + 202804, + 29379, + 64241, + 132121, + 790622, + 493588, + 0, + 48, + 147352, + 925197, + 38149, + 18380, + 0, + 270280, + 633, + 3373, + 31294, + 7830, + 0, + 0, + 11371, + 56143, + 5393, + 74724, + 495109, + 0, + 18993, + 21524, + 0, + 53889, + 400509, + 204563, + 0, + 11625, + 9635, + 0, + 1678, + 12096, + 59, + 817112, + 10002, + 128209, + 11593, + 17313, + 15200, + 106796, + 261401, + 707077, + 0, + 314030, + 798591, + 14175, + 5668, + 2766, + 0, + 566, + 5543, + 24112, + 154482, + 5642, + 0, + 38410, + 3, + 4, + 700724, + 25024, + 5, + 407, + 564150, + 672, + 143, + 2049, + 574708, + 65858, + 213412, + 3797, + 511, + 30907, + 1212, + 765, + 2127, + 481, + 130048, + 113816, + 39861, + 153169, + 503378, + 523944, + 111, + 55083, + 698, + 275, + 3, + 3195, + 1657, + 0, + 317881, + 6672, + 543, + 153011, + 77240, + 9338, + 889850, + 29518, + 872485, + 181927, + 376086, + 266, + 409, + 4, + 14856, + 31943, + 2448, + 8, + 75, + 383097, + 294366, + 0, + 173084, + 753160, + 66457, + 725783, + 51, + 127651, + 1073, + 12598, + 140080, + 0, + 296375, + 581720, + 217346, + 8272, + 2051, + 185390, + 520645, + 1260, + 13873, + 168040, + 19690, + 103347, + 295011, + 548404, + 48, + 4, + 916417, + 1948, + 621365, + 263245, + 2792, + 86803, + 181193, + 558081, + 50907, + 442770, + 51448, + 340276, + 1346, + 607, + 459627, + 0, + 30, + 73298, + 15389, + 12264, + 2719, + 2936, + 143043, + 209970, + 0, + 42, + 6657, + 317419, + 0, + 32622, + 524000, + 0, + 310331, + 303778, + 268710, + 9, + 10410, + 25343, + 949506, + 784353, + 3861, + 46823, + 251292, + 75008, + 269798, + 87731, + 112813, + 571679, + 385, + 3, + 2811, + 36025, + 9243, + 935128, + 906, + 10688, + 25, + 86757, + 307, + 55, + 22, + 2, + 61, + 620426, + 484530, + 633806, + 0, + 1342, + 9293, + 992181, + 503, + 195433, + 46150, + 893091, + 3207, + 2865, + 72894, + 830299, + 355, + 327479, + 0, + 35573, + 3068, + 15699, + 31187, + 55378, + 416067, + 91721, + 159, + 0, + 255139, + 2104, + 19, + 606757, + 323, + 902659, + 365655, + 400, + 903, + 408, + 385, + 21774, + 701290, + 234426, + 17020, + 950, + 0, + 0, + 429, + 1245, + 405871, + 1097, + 280634, + 74, + 158233, + 1583, + 180333, + 42114, + 575973, + 539327, + 59252, + 121928, + 165, + 148501, + 55757, + 7494, + 127728, + 7832, + 68504, + 619770, + 70995, + 312816, + 7307, + 38265, + 46248, + 363304, + 269442, + 77112, + 448331, + 910442, + 474418, + 152752, + 752, + 104912, + 408492, + 691709, + 632381, + 48519, + 20524, + 344294, + 14670, + 0, + 21607, + 81162, + 181458, + 0, + 908322, + 7261, + 10888, + 58054, + 1788, + 970933, + 5925, + 121553, + 36152, + 588267, + 23615, + 1850, + 30728, + 3599, + 1319, + 6027, + 0, + 32141, + 984156, + 436781, + 15003, + 621407, + 9412, + 562911, + 189740, + 377895, + 656800, + 197, + 14413, + 99382, + 384, + 11480, + 0, + 86118, + 881961, + 1905, + 82061, + 4140, + 741153, + 26, + 687, + 12251, + 10945, + 209267, + 220602, + 135881, + 6, + 237945, + 158, + 5, + 76303, + 81344, + 986042, + 956063, + 30282, + 186055, + 357802, + 12492, + 577476, + 838, + 0, + 11, + 117602, + 0, + 187928, + 96860, + 4268, + 3478, + 818264, + 1649, + 17175, + 272, + 158951, + 440987, + 677594, + 14935, + 37953, + 0, + 198, + 160404, + 12, + 287803, + 2386, + 10, + 271663, + 319152, + 361322, + 68370, + 428, + 182707, + 387429, + 1152, + 360065, + 25218, + 2790, + 42228, + 13, + 110942, + 452491, + 1, + 665638, + 2308, + 1196, + 87306, + 66, + 219, + 0, + 130736, + 334, + 605, + 5979, + 2681, + 0, + 123463, + 11219, + 283681, + 19269, + 553, + 6217, + 130965, + 714409, + 242, + 674833, + 237581, + 133284, + 683, + 1758, + 278193, + 518726, + 44, + 420361, + 325228, + 14955, + 10, + 11994, + 64157, + 1937, + 20214, + 848, + 27804, + 151341, + 79236, + 316393, + 158883, + 1196, + 334, + 22797, + 185955, + 13857, + 397357, + 7948, + 6038, + 0, + 2621, + 16, + 155267, + 44809, + 9171, + 21328, + 12212, + 40200, + 2600, + 439, + 804014, + 10938, + 96135, + 43696, + 158715, + 4, + 284558, + 191, + 270254, + 7923, + 880603, + 21032, + 107700, + 172, + 700823, + 5613, + 78816, + 258290, + 214398, + 821856, + 295325, + 0, + 1, + 23559, + 63895, + 21249, + 717490, + 956952, + 944819, + 793, + 356, + 757716, + 111773, + 394826, + 25665, + 4358, + 640216, + 1152, + 37175, + 150192, + 106071, + 28992, + 67, + 1685, + 134242, + 2, + 102045, + 1457, + 419589, + 6789, + 677, + 94675, + 11300, + 2595, + 8, + 926535, + 265194, + 0, + 886048, + 246242, + 1494, + 191, + 169985, + 649765, + 0, + 201, + 1069, + 679163, + 16627, + 274639, + 84438, + 3, + 1301, + 247496, + 5879, + 710904, + 403652, + 958241, + 361, + 139732, + 6042, + 15985, + 2378, + 267031, + 223767, + 9656, + 241717, + 33863, + 14314, + 205697, + 1274, + 168000, + 621777, + 837913, + 89654, + 659829, + 69, + 503884, + 432717, + 70443, + 110891, + 19655, + 132432, + 620401, + 428, + 0, + 425662, + 0, + 0, + 0, + 194489, + 7601, + 26870, + 0, + 63, + 594, + 12278, + 582479, + 213723, + 424489, + 96446, + 990664, + 46966, + 44137, + 829810, + 104, + 19707, + 16, + 0, + 2499, + 167075, + 140972, + 249283, + 6620, + 68368, + 856414, + 9255, + 14315, + 0, + 11432, + 24329, + 216463, + 299556, + 818401, + 246607, + 697733, + 229, + 144, + 389394, + 664634, + 0, + 19393, + 657903, + 52912, + 952177, + 536931, + 187271, + 17687, + 970155, + 232571, + 234016, + 159980, + 13510, + 32952, + 0, + 0, + 24132, + 18806, + 15624, + 28364, + 472126, + 626978, + 599, + 112843, + 502933, + 915660, + 63920, + 0, + 84, + 10899, + 904823, + 126, + 469132, + 590052, + 195831, + 443113, + 294149, + 15944, + 2271, + 282974, + 211, + 0, + 22934, + 82283, + 49973, + 41707, + 87530, + 0, + 910528, + 0, + 36029, + 423337, + 817512, + 223671, + 27800, + 398847, + 198528, + 1, + 560679, + 518270, + 23033, + 501059, + 0, + 3909, + 272062, + 261581, + 187, + 52043, + 334, + 24354, + 3947, + 8549, + 37863, + 328851, + 963771, + 1, + 3930, + 82416, + 6, + 2943, + 122101, + 82577, + 85, + 89540, + 5135, + 109236, + 18297, + 1, + 177371, + 4541, + 769577, + 178, + 417, + 960566, + 33803, + 911651, + 248160, + 153725, + 43981, + 809174, + 116, + 486900, + 4842, + 148490, + 131534, + 4347, + 239949, + 984096, + 749756, + 429499, + 2794, + 78209, + 18812, + 21111, + 490, + 328042, + 12, + 132119, + 505103, + 353148, + 0, + 373656, + 951244, + 491, + 355778, + 30620, + 317, + 60175, + 220, + 214496, + 41249, + 5169, + 78367, + 506804, + 0, + 1368, + 407, + 295126, + 1288, + 86, + 97614, + 61640, + 244723, + 3, + 0, + 869827, + 527246, + 52, + 107036, + 240739, + 780281, + 113084, + 62009, + 740343, + 483201, + 8649, + 16419, + 1, + 801574, + 95524, + 326126, + 26912, + 877040, + 10262, + 5895, + 0, + 132633, + 59171, + 306347, + 702701, + 196245, + 12642, + 32723, + 24608, + 30287, + 45775, + 18281, + 7587, + 144532, + 5, + 35, + 99862, + 215127, + 170875, + 61461, + 77790, + 5, + 0, + 129358, + 0, + 105084, + 21399, + 42233, + 85397, + 480654, + 555988, + 89575, + 42346, + 20004, + 11102, + 21321, + 185, + 379267, + 849147, + 121514, + 3388, + 33662, + 12, + 164898, + 226, + 274, + 385003, + 365052, + 693376, + 41245, + 9010, + 41594, + 89835, + 10490, + 272, + 128437, + 0, + 122648, + 277, + 116505, + 38372, + 4, + 1376, + 0, + 46317, + 139368, + 36398, + 193899, + 30632, + 26371, + 7548, + 367643, + 954849, + 25889, + 36567, + 176, + 140631, + 4690, + 975031, + 80965, + 500471, + 8442, + 43, + 27758, + 301501, + 3797, + 80, + 384440, + 928477, + 4960, + 24566, + 33245, + 14638, + 228354, + 54347, + 861285, + 12841, + 2, + 157402, + 646747, + 53763, + 1, + 214732, + 49471, + 49757, + 998, + 201135, + 566, + 73512, + 194240, + 391773, + 21510, + 13, + 829894, + 783200, + 565329, + 2101, + 12, + 191043, + 1621, + 18443, + 279, + 294135, + 526503, + 729735, + 4639, + 444138, + 5835, + 12372, + 46362, + 1543, + 870907, + 83262, + 0, + 38331, + 95, + 1194, + 909, + 8053, + 453066, + 845561, + 411, + 3229, + 1, + 158, + 1431, + 835137, + 21774, + 7298, + 148388, + 224649, + 379318, + 520138, + 39781, + 172130, + 362634, + 487495, + 51957, + 158, + 1770, + 7, + 18010, + 1063, + 171484, + 19924, + 279867, + 469956, + 189785, + 0, + 814, + 60580, + 944349, + 18743, + 553235, + 0, + 95475, + 99, + 0, + 5, + 42623, + 178418, + 398940, + 5700, + 69023, + 5786, + 0, + 10531, + 551, + 86308, + 63451, + 32704, + 176903, + 0, + 251689, + 11589, + 25711, + 43437, + 1431, + 304, + 52965, + 34816, + 268688, + 47756, + 825323, + 122608, + 81246, + 69974, + 360515, + 99973, + 143015, + 5063, + 4499, + 34459, + 171982, + 677943, + 489082, + 257515, + 3765, + 5, + 7416, + 602206, + 74122, + 3, + 686204, + 5493, + 28901, + 11349, + 549668, + 257082, + 82000, + 17031, + 1517, + 7442, + 937160, + 722, + 0, + 72952, + 377192, + 438266, + 555, + 31436, + 284, + 56390, + 0, + 585856, + 27635, + 519344, + 126131, + 360273, + 845073, + 0, + 191965, + 55652, + 23, + 112773, + 639025, + 84749, + 0, + 330822, + 7173, + 126217, + 871, + 112112, + 0, + 664, + 530474, + 1, + 379564, + 172617, + 647308, + 0, + 356, + 17, + 84345, + 457, + 0, + 8, + 6, + 136602, + 634424, + 0, + 177298, + 100726, + 91661, + 383792, + 1665, + 43583, + 15775, + 4083, + 4277, + 345749, + 969599, + 65804, + 19327, + 0, + 352514, + 4225, + 9, + 103767, + 0, + 0, + 148436, + 850, + 33, + 2146, + 20153, + 50, + 9063, + 50329, + 348379, + 2569, + 83697, + 37073, + 715486, + 629, + 4753, + 442, + 259203, + 287223, + 48625, + 9, + 70184, + 45946, + 144947, + 0, + 60285, + 28640, + 7626, + 134159, + 33, + 12452, + 150566, + 348293, + 124426, + 353952, + 11, + 22, + 776742, + 29072, + 132168, + 254533, + 319957, + 1602, + 1659, + 209341, + 32847, + 92392, + 753005, + 1392, + 10271, + 28557, + 6717, + 941745, + 0, + 0, + 0, + 78645, + 45320, + 11193, + 1448, + 130626, + 377907, + 795535, + 24285, + 26094, + 266691, + 64449, + 77400, + 191410, + 1, + 1346, + 25224, + 489637, + 47052, + 248592, + 76689, + 0, + 7722, + 47285, + 3152, + 285577, + 0, + 149366, + 264346, + 1, + 208602, + 320459, + 131771, + 1421, + 350, + 723283, + 714934, + 0, + 566439, + 11656, + 34189, + 125484, + 943273, + 15, + 7789, + 0, + 7427, + 464278, + 680924, + 651102, + 87794, + 39640, + 838644, + 964500, + 1, + 1765, + 272604, + 10, + 837347, + 44845, + 130, + 163357, + 4150, + 403331, + 839132, + 44876, + 272792, + 592527, + 57225, + 128826, + 2915, + 2, + 3570, + 2410, + 199, + 171358, + 5931, + 53620, + 55299, + 1868, + 24123, + 165, + 346513, + 16527, + 133, + 517412, + 195700, + 730365, + 896209, + 152760, + 24577, + 65, + 8218, + 349642, + 901345, + 5127, + 5102, + 238318, + 955, + 631921, + 12218, + 55101, + 930381, + 219503, + 469237, + 132, + 16701, + 494, + 199729, + 0, + 32139, + 314, + 172, + 2947, + 106997, + 4871, + 236, + 6146, + 1843, + 128, + 0, + 254240, + 2964, + 14825, + 60624, + 2108, + 286953, + 654931, + 0, + 0, + 396587, + 19852, + 70311, + 363561, + 282, + 17966, + 924254, + 104173, + 130816, + 179096, + 105466, + 136, + 618261, + 358433, + 25587, + 49357, + 102, + 133746, + 620776, + 17084, + 406881, + 802675, + 349, + 69, + 8761, + 278482, + 16336, + 128, + 160096, + 25857, + 280, + 39639, + 726299, + 293905, + 4621, + 41, + 649, + 3655, + 269286, + 578026, + 0, + 11156, + 1, + 744858, + 531, + 48155, + 28435, + 7991, + 447, + 10201, + 379341, + 0, + 5773, + 0, + 295, + 228592, + 331155, + 104089, + 628069, + 29693, + 22, + 13, + 0, + 0, + 554349, + 6082, + 238, + 23, + 151873, + 805937, + 0, + 194076, + 6450, + 3, + 128322, + 69149, + 95511, + 86, + 844368, + 415964, + 51985, + 308686, + 553403, + 624943, + 365800, + 4, + 120263, + 91239, + 195248, + 58010, + 19, + 415112, + 136806, + 42, + 571848, + 55306, + 29454, + 3, + 144926, + 189, + 0, + 161943, + 592155, + 10930, + 279297, + 56932, + 957430, + 10244, + 190296, + 807209, + 781, + 1466, + 235055, + 33, + 196, + 58280, + 436, + 408649, + 221, + 711143, + 10495, + 2441, + 275720, + 2, + 15391, + 132107, + 102610, + 688549, + 237142, + 3041, + 14, + 308623, + 0, + 0, + 287, + 295147, + 61443, + 229, + 207, + 2051, + 64, + 13479, + 55656, + 570134, + 50387, + 225869, + 20615, + 258465, + 64932, + 112461, + 164521, + 907269, + 758563, + 22901, + 0, + 7944, + 48, + 154921, + 2784, + 548608, + 0, + 12524, + 142556, + 0, + 13882, + 507227, + 316598, + 987551, + 0, + 894687, + 1964, + 364, + 10316, + 440269, + 9, + 776723, + 72288, + 54604, + 185101, + 142, + 362, + 11679, + 77, + 79, + 529321, + 364, + 42387, + 0, + 570879, + 417503, + 604871, + 578806, + 1102, + 66584, + 615440, + 146744, + 19441, + 170478, + 144069, + 36170, + 145376, + 842283, + 193612, + 3, + 359429, + 368596, + 0, + 11064, + 7726, + 229410, + 63569, + 67402, + 91, + 203201, + 213513, + 0, + 704479, + 1325, + 0, + 385154, + 13, + 806763, + 197132, + 6183, + 45760, + 99377, + 0, + 972077, + 4043, + 195700, + 34229, + 0, + 154027, + 633, + 6, + 32142, + 0, + 29, + 620842, + 14099, + 495465, + 26937, + 0, + 0, + 432, + 227704, + 0, + 63, + 0, + 19, + 863491, + 20, + 1, + 160713, + 24607, + 85800, + 3566, + 37854, + 81913, + 121573, + 816, + 20, + 133253, + 692231, + 4869, + 255175, + 15028, + 9383, + 542877, + 4608, + 369610, + 243635, + 385285, + 391565, + 286009, + 0, + 61685, + 416318, + 208, + 67019, + 788416, + 88, + 165056, + 0, + 439589, + 160, + 105528, + 152, + 160624, + 865, + 390229, + 714086, + 6007, + 30229, + 481306, + 173266, + 1135, + 2266, + 8, + 59, + 104722, + 647885, + 579471, + 21309, + 230834, + 140278, + 31858, + 3288, + 36011, + 151387, + 594217, + 22439, + 418638, + 76859, + 29363, + 154809, + 275533, + 39, + 472996, + 22076, + 7481, + 155705, + 10406, + 214779, + 223, + 1312, + 16391, + 17203, + 55605, + 44579, + 69332, + 303, + 19217, + 26288, + 126212, + 316, + 98, + 114, + 37382, + 137591, + 439749, + 12972, + 54, + 154879, + 0, + 102680, + 7639, + 309119, + 263550, + 766, + 1124, + 56, + 686608, + 123767, + 518054, + 18, + 672385, + 3161, + 53791, + 26769, + 451670, + 61, + 148245, + 2713, + 96725, + 4794, + 33247, + 297946, + 33380, + 0, + 20034, + 5647, + 17227, + 76444, + 0, + 21011, + 675, + 13226, + 1027, + 990842, + 124459, + 34406, + 53, + 69540, + 134, + 0, + 168521, + 6, + 4075, + 1137, + 63740, + 220, + 10434, + 1171, + 28950, + 0, + 79680, + 993269, + 355622, + 15, + 0, + 1452, + 21667, + 22208, + 494484, + 33984, + 691308, + 10, + 693686, + 196, + 9, + 70676, + 157660, + 775, + 165, + 468432, + 1083, + 515154, + 778344, + 70241, + 42, + 40931, + 277125, + 43837, + 301881, + 1332, + 56712, + 9013, + 1299, + 7564, + 31092, + 1975, + 113517, + 833295, + 245021, + 36503, + 23586, + 149327, + 89175, + 10512, + 484348, + 187793, + 954609, + 53199, + 792175, + 126, + 12369, + 405, + 0, + 6614, + 322857, + 166, + 571874, + 60839, + 180975, + 146722, + 411565, + 1536, + 1, + 11, + 116230, + 60514, + 9003, + 2325, + 43763, + 63, + 355553, + 0, + 389876, + 14672, + 11526, + 160209, + 65, + 10283, + 966, + 10, + 58333, + 129920, + 2850, + 83346, + 0, + 14, + 295819, + 679550, + 143928, + 29489, + 82324, + 36558, + 267118, + 143313, + 90107, + 12789, + 951, + 0, + 187619, + 295317, + 82, + 41326, + 309682, + 907327, + 809358, + 324, + 139157, + 12, + 78366, + 671811, + 354, + 131, + 70525, + 35830, + 281018, + 91456, + 92523, + 54874, + 48273, + 2423, + 0, + 81, + 361314, + 374811, + 394758, + 15350, + 795, + 3, + 16779, + 796684, + 477556, + 73927, + 26643, + 119281, + 62692, + 17039, + 454778, + 952, + 48973, + 19529, + 151, + 239121, + 93509, + 254702, + 1307, + 10029, + 7973, + 546706, + 806644, + 680517, + 223, + 0, + 2, + 0, + 402421, + 619193, + 15685, + 2, + 939715, + 519198, + 0, + 444312, + 23204, + 35669, + 32467, + 0, + 799725, + 5883, + 2217, + 32292, + 355557, + 22179, + 1066, + 15704, + 610, + 37819, + 403626, + 83101, + 10989, + 311607, + 43394, + 72576, + 335450, + 85964, + 73734, + 105142, + 38292, + 0, + 181516, + 33959, + 611797, + 221838, + 5931, + 7666, + 1044, + 477173, + 13591, + 405, + 521, + 190653, + 184191, + 0, + 215, + 847195, + 22782, + 11912, + 27345, + 2572, + 0, + 566350, + 7, + 52302, + 26641, + 587826, + 127, + 2, + 44449, + 153198, + 14, + 926, + 285, + 0, + 938196, + 52255, + 9153, + 807, + 12548, + 358324, + 18521, + 104956, + 42738, + 116, + 135772, + 189554, + 38, + 54, + 36, + 89768, + 17170, + 75, + 34502, + 45489, + 172796, + 971810, + 16153, + 499280, + 1, + 879663, + 53830, + 186, + 539, + 242059, + 268, + 402, + 2732, + 68057, + 18463, + 198560, + 10068, + 591753, + 6116, + 699280, + 1, + 0, + 114258, + 277, + 149, + 283821, + 352561, + 88172, + 684476, + 3450, + 87, + 99936, + 3155, + 72983, + 31619, + 8832, + 58666, + 0, + 59023, + 306091, + 352150, + 255063, + 992708, + 23, + 4896, + 18165, + 424401, + 227613, + 5175, + 347, + 139846, + 11962, + 714, + 3501, + 82367, + 11110, + 10, + 12874, + 0, + 0, + 222712, + 169, + 123281, + 0, + 268149, + 101, + 17446, + 4262, + 489, + 0, + 30, + 0, + 277235, + 28, + 71, + 23, + 61219, + 953631, + 477548, + 662491, + 273, + 44787, + 4130, + 14483, + 470571, + 735977, + 406648, + 815898, + 5985, + 462696, + 937510, + 9, + 0, + 111727, + 93, + 331435, + 336402, + 78690, + 49, + 0, + 87422, + 1242, + 0, + 8783, + 8540, + 314, + 33411, + 805718, + 247, + 6870, + 523743, + 8323, + 612593, + 430, + 354048, + 264913, + 83, + 114063, + 202825, + 35202, + 32823, + 185554, + 85760, + 45159, + 5971, + 267733, + 4545, + 116, + 6910, + 24833, + 218, + 922362, + 221735, + 740, + 7112, + 31, + 15739, + 523589, + 4, + 95996, + 936, + 823951, + 0, + 88, + 160, + 375419, + 663627, + 3741, + 22896, + 114326, + 415962, + 880100, + 6222, + 18650, + 35524, + 195076, + 506, + 451640, + 541336, + 70903, + 3946, + 1, + 61765, + 1, + 2696, + 753129, + 289, + 225234, + 378692, + 1703, + 6751, + 1, + 820, + 7677, + 589, + 12412, + 317, + 69, + 226031, + 134523, + 318253, + 66677, + 111025, + 96, + 0, + 96, + 523528, + 1017, + 0, + 258740, + 420947, + 4600, + 400684, + 12174, + 11770, + 52, + 5959, + 82658, + 531787, + 202, + 548430, + 964, + 1054, + 34, + 96897, + 25445, + 47609, + 386052, + 97004, + 1935, + 30074, + 13458, + 494105, + 54, + 65575, + 594698, + 2340, + 20259, + 84, + 2774, + 534, + 972534, + 115057, + 0, + 11379, + 0, + 271, + 266305, + 132595, + 2, + 773561, + 52365, + 3585, + 351, + 148206, + 778964, + 149379, + 596, + 284914, + 2900, + 35596, + 1547, + 212027, + 8100, + 12248, + 3013, + 1814, + 183415, + 273633, + 15812, + 0, + 966680, + 14830, + 134309, + 0, + 416450, + 206611, + 816, + 82258, + 9873, + 3155, + 53485, + 779805, + 107690, + 254475, + 102504, + 72495, + 17301, + 472130, + 6895, + 245420, + 7299, + 110508, + 27776, + 246134, + 0, + 330853, + 0, + 271767, + 61886, + 24123, + 309681, + 58325, + 608865, + 20666, + 87349, + 229228, + 246, + 457768, + 5374, + 69643, + 148, + 618375, + 45236, + 352565, + 133904, + 152, + 10688, + 18, + 0, + 276036, + 493281, + 11156, + 12566, + 5762, + 113, + 24179, + 98, + 327, + 893, + 209180, + 140805, + 0, + 2341, + 66309, + 30305, + 630559, + 3682, + 152767, + 265822, + 142868, + 1535, + 728603, + 69081, + 353151, + 237995, + 1075, + 925071, + 86, + 6748, + 0, + 684186, + 735, + 13793, + 4790, + 73175, + 69677, + 367627, + 238650, + 303543, + 1, + 26059, + 21392, + 10, + 288609, + 0, + 76345, + 158496, + 7000, + 1865, + 20385, + 0, + 54213, + 9948, + 102667, + 6963, + 71, + 555744, + 5626, + 2512, + 1124, + 7171, + 628, + 29225, + 321687, + 61519, + 4, + 8352, + 9156, +}; + +char *pointers[NCYCLES]; + +int main(void) +{ + int r, i, j, sp, sq; + char *p, *q, *ep, *eq; + int ok; + int err = 0; + + for (r = 0; r < 4; r++) { + for (i = 0; i < NCYCLES; i++) { + pointers[i] = p = malloc(sp = sizes[i]); + ep = p + sp; + ok = 1; + for (j = 0; j < i; j++) { + q = pointers[j]; + sq = sizes[j]; + eq = q + sq; + + if ((p < q && ep > q) || (p >= q && p < eq)) { + ok = 0; + err = 1; + break; + } + } + printf("Allocated %6d bytes at %p, ok = %d\n", sp, p, + ok); + + if (p) + memset(p, 0xee, sp); /* Poison this memory */ + } + + for (i = 0; i < NCYCLES; i++) { + free(pointers[i]); + printf("Freed %6d bytes at %p\n", sizes[i], + pointers[i]); + } + } + + return err; +} diff --git a/usr/klibc/tests/malloctest2.c b/usr/klibc/tests/malloctest2.c new file mode 100644 index 0000000..1845073 --- /dev/null +++ b/usr/klibc/tests/malloctest2.c @@ -0,0 +1,62 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define NCYCLES 32768 +#define NSLOTS 4096 + +struct slot { + char *ptr; + size_t size; +}; + +struct slot s[NSLOTS]; + +int main(void) +{ + size_t sp, sq; + char *p, *ep, *q, *eq; + int r, i, j; + int ok; + int err = 0; + + for (r = 0; r < NCYCLES; r++) { + i = lrand48() % NSLOTS; + + if (s[i].ptr) { + free(s[i].ptr); + printf("Freed %8zu bytes at %p\n", s[i].size, + s[i].ptr); + s[i].ptr = NULL; + s[i].size = 0; + } else { + sp = lrand48(); /* 32-bit random number */ + sp >>= 12 + (lrand48() % 20); + + s[i].size = sp; + s[i].ptr = p = malloc(sp); + ep = p + sp; + ok = 1; + for (j = 0; j < NSLOTS; j++) { + q = s[j].ptr; + if (i != j && q) { + sq = s[j].size; + eq = q + sq; + + if ((p < q && ep > q) + || (p >= q && p < eq)) { + ok = 0; + err = 1; + break; + } + } + } + printf("Allocated %8zu bytes at %p, ok = %d\n", sp, p, + ok); + + if (p) + memset(p, 0xee, sp); /* Poison this memory */ + } + } + return err; +} diff --git a/usr/klibc/tests/malloctest3.c b/usr/klibc/tests/malloctest3.c new file mode 100644 index 0000000..d9d2ca9 --- /dev/null +++ b/usr/klibc/tests/malloctest3.c @@ -0,0 +1,57 @@ +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +int main(void) +{ + void *p; + + /* Our implementation should always return NULL */ + errno = 0; + p = malloc(0); + assert(p == NULL); + assert(errno == 0); + + /* These sizes won't fit in memory, so should always fail */ + errno = 0; + p = malloc(SIZE_MAX); + assert(p == NULL); + assert(errno == ENOMEM); + errno = 0; + p = malloc(SIZE_MAX - 0x10000); + assert(p == NULL); + assert(errno == ENOMEM); + +#if SIZE_MAX > 0x100000000 + /* We should be able to allocate 4 GB + 1 */ + p = malloc(0x100000001); + assert(p != NULL); + ((volatile char *)p)[0x100000000] = 1; + free(p); + + /* calloc() should detect multiplication overflow */ + errno = 0; + p = calloc(0x100000000, 0x100000000); + assert(p == NULL); + assert(errno == ENOMEM); + errno = 0; + p = calloc(0x100000001, 0x100000001); + assert(p == NULL); + assert(errno == ENOMEM); +#else + /* calloc() should detect multiplication overflow */ + errno = 0; + p = calloc(0x10000, 0x10000); + assert(p == NULL); + assert(errno == ENOMEM); + errno = 0; + p = calloc(0x10001, 0x10001); + assert(p == NULL); + assert(errno == ENOMEM); +#endif + + return 0; +} diff --git a/usr/klibc/tests/memstrtest.c b/usr/klibc/tests/memstrtest.c new file mode 100644 index 0000000..8d473fb --- /dev/null +++ b/usr/klibc/tests/memstrtest.c @@ -0,0 +1,28 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +int main(void) +{ + unsigned char t1[256], t2[256]; + int i; + int r; + + for (i = 0; i < (int)sizeof(t1); i++) + t1[i] = t2[i] = (unsigned char)i; + + r = memcmp(t1, t2, sizeof(t1)); + printf("memcmp r = %d\n", r); + r = memcmp(t1, t2, sizeof(t1) / 2); + printf("memcmp r = %d\n", r); + t1[255] = 0; + r = memcmp(t1, t2, sizeof(t1)); + printf("memcmp r = %d\n", r); + + for (i = 0; i < (int)sizeof(t1); i++) + t1[i] = 0xaa; + memset(t2, 0xaa, sizeof(t2)); + r = memcmp(t1, t2, sizeof(t1)); + printf("memcmp r = %d\n", r); + return 0; +} diff --git a/usr/klibc/tests/microhello.c b/usr/klibc/tests/microhello.c new file mode 100644 index 0000000..c999d11 --- /dev/null +++ b/usr/klibc/tests/microhello.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include <unistd.h> + +int main(void) +{ + static const char hello[] = "Hello, World!\n"; + _fwrite(hello, sizeof hello - 1, stdout); + return 0; +} diff --git a/usr/klibc/tests/minihello.c b/usr/klibc/tests/minihello.c new file mode 100644 index 0000000..ae9072e --- /dev/null +++ b/usr/klibc/tests/minihello.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int main(void) +{ + fputs("Hello, World!\n", stdout); + return 0; +} diff --git a/usr/klibc/tests/mmaptest.c b/usr/klibc/tests/mmaptest.c new file mode 100644 index 0000000..eac04e8 --- /dev/null +++ b/usr/klibc/tests/mmaptest.c @@ -0,0 +1,73 @@ +/* + * mmaptest.c + * + * Test some simple cases of mmap() + */ + +#include <sys/mman.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <fcntl.h> + +static void make_test_file(int fd) +{ + unsigned long v; + FILE *f = fdopen(fd, "wb"); + + for (v = 0; v < 262144; v += sizeof(v)) + _fwrite(&v, sizeof(v), f); +} + +int main(int argc, char *argv[]) +{ + void *foo; + char *test_file = (argc > 1) ? argv[1] : "/tmp/mmaptest.tmp"; + int rv, fd; + + /* Important case, this is how we get memory for malloc() */ + errno = 0; + foo = mmap(NULL, 65536, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + + printf("mmap() returned %p, errno = %d\n", foo, errno); + if (foo == MAP_FAILED) + return 1; + + rv = munmap(foo, 65536); + printf("munmap() returned %d, errno = %d\n", rv, errno); + if (rv) + return 1; + + /* Create test file */ + fd = open(test_file, O_RDWR | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + perror(test_file); + return 1; + } + + make_test_file(fd); + + /* Map test file */ + foo = mmap(NULL, 65536, PROT_READ, MAP_SHARED, fd, 131072); + printf("mmap() returned %p, errno = %d\n", foo, errno); + if (foo == MAP_FAILED) + return 1; + + if (*(unsigned long *)foo != 131072) { + printf("mmap() with offset returned the wrong offset %ld!\n", + *(unsigned long *)foo); + return 1; + } + + if (munmap(foo, 65536)) { + printf("munmap() returned nonzero, errno = %d\n", errno); + return 1; + } + + close(fd); + unlink(test_file); + + return 0; +} diff --git a/usr/klibc/tests/opentest.c b/usr/klibc/tests/opentest.c new file mode 100644 index 0000000..8be7b3d --- /dev/null +++ b/usr/klibc/tests/opentest.c @@ -0,0 +1,15 @@ +#include <stdio.h> + +int main(void) +{ + char buffer[1024]; + FILE *f; + + f = fopen("/etc/passwd", "r"); + fgets(buffer, 1024, f); + fclose(f); + + printf("Line 1 = %s", buffer); + + return 0; +} diff --git a/usr/klibc/tests/pipetest.c b/usr/klibc/tests/pipetest.c new file mode 100644 index 0000000..0e49721 --- /dev/null +++ b/usr/klibc/tests/pipetest.c @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> + +int main(void) +{ + /* Size of message must be <= PIPE_BUF */ + const char msg[] = "Hello, World!"; + char buf[512]; + int rv; + int pfd[2]; + + if (pipe(pfd)) { + perror("pipe"); + return 1; + } + + if (write(pfd[1], msg, sizeof msg) != sizeof msg) { + perror("write"); + return 1; + } + + while ((rv = read(pfd[0], buf, sizeof buf)) < sizeof msg) { + if (rv == -1 && errno == EINTR) + continue; + perror("read"); + return 1; + } + + if (memcmp(msg, buf, sizeof msg)) { + fprintf(stderr, "Message miscompare!\n"); + return 1; + } + + return 0; +} diff --git a/usr/klibc/tests/rtsig.c b/usr/klibc/tests/rtsig.c new file mode 100644 index 0000000..a7f8eed --- /dev/null +++ b/usr/klibc/tests/rtsig.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <signal.h> + +int main(void) +{ +#ifdef SIGRTMIN + printf("sigrtmin = %d, sigrtmax = %d\n", SIGRTMIN, SIGRTMAX); +#else + printf("No realtime signals\n"); +#endif + return 0; +} diff --git a/usr/klibc/tests/select.c b/usr/klibc/tests/select.c new file mode 100644 index 0000000..ba08112 --- /dev/null +++ b/usr/klibc/tests/select.c @@ -0,0 +1,55 @@ +/* + * Simple test of select() + */ + +#include <sys/select.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +int main(int argc, char *argv[]) +{ + int fdn, fdz, pfd[2], rv; + fd_set readset; + struct timeval timeout; + int err = 0; + + /* We can always read from /dev/zero; open a pipe that is never + ready for a "never readable" file descriptor */ + fdz = open("/dev/zero", O_RDONLY); + pipe(pfd); + fdn = pfd[0]; + + FD_ZERO(&readset); + FD_SET(fdn, &readset); + + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + + rv = select(FD_SETSIZE, &readset, NULL, NULL, &timeout); + + if (rv != 0) { + fprintf(stderr, + "select with timeout failed (rv = %d, errno = %s)\n", + rv, strerror(errno)); + err++; + } + + FD_ZERO(&readset); + FD_SET(fdn, &readset); + FD_SET(fdz, &readset); + + rv = select(FD_SETSIZE, &readset, NULL, NULL, &timeout); + + if (rv != 1 || !FD_ISSET(fdz, &readset) || + FD_ISSET(fdn, &readset)) { + fprintf(stderr, + "select with /dev/zero failed (rv = %d, errno = %s)\n", + rv, strerror(errno)); + err++; + } + + return err; +} diff --git a/usr/klibc/tests/setenvtest.c b/usr/klibc/tests/setenvtest.c new file mode 100644 index 0000000..1b07199 --- /dev/null +++ b/usr/klibc/tests/setenvtest.c @@ -0,0 +1,39 @@ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +int main(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + + /* Set SETENV */ + setenv("SETENV", "setenv", 1); + + /* Set PUTENV */ + putenv("PUTENV=putenv"); + + /* Print the results... */ + printf("SETENV = %s\n", getenv("SETENV")); + printf("PUTENV = %s\n", getenv("PUTENV")); + + /* Override tests */ + setenv("SETENV", "setenv_good", 1); + putenv("PUTENV=putenv_good"); + printf("SETENV = %s\n", getenv("SETENV")); + printf("PUTENV = %s\n", getenv("PUTENV")); + + /* Non-override test */ + setenv("SETENV", "setenv_bad", 0); + setenv("NEWENV", "newenv_good", 0); + printf("SETENV = %s\n", getenv("SETENV")); + printf("NEWENV = %s\n", getenv("NEWENV")); + + /* Undef test */ + unsetenv("SETENV"); + unsetenv("NEWENV"); + printf("SETENV = %s\n", getenv("SETENV")); + printf("NEWENV = %s\n", getenv("NEWENV")); + + return 0; +} diff --git a/usr/klibc/tests/setjmptest.c b/usr/klibc/tests/setjmptest.c new file mode 100644 index 0000000..fd9cb1c --- /dev/null +++ b/usr/klibc/tests/setjmptest.c @@ -0,0 +1,37 @@ +/* + * setjmptest.c + */ + +#include <stdio.h> +#include <setjmp.h> + +static jmp_buf buf; + +void do_stuff(int v) +{ + printf("calling longjmp with %d... ", v + 1); + longjmp(buf, v + 1); +} + +void recurse(int ctr, int v) +{ + if (ctr--) + recurse(ctr, v); + else + do_stuff(v); + + printf("ERROR!\n"); /* We should never get here... */ +} + +int main(void) +{ + int v; + + v = setjmp(buf); + printf("setjmp returned %d\n", v); + + if (v < 256) + recurse(v, v); + + return 0; +} diff --git a/usr/klibc/tests/sig-nodefer.c b/usr/klibc/tests/sig-nodefer.c new file mode 100644 index 0000000..90086e4 --- /dev/null +++ b/usr/klibc/tests/sig-nodefer.c @@ -0,0 +1,55 @@ +/* + * Expected output of ./sig-nodefer: + * SIGUSR2: blocked + * SIGTERM: blocked + */ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +void handler(int signum) +{ + sigset_t mask; + + sigprocmask(SIG_BLOCK, NULL, &mask); + printf("SIGUSR2: %s\n", + sigismember(&mask, SIGUSR2) ? "blocked" : "not blocked"); + printf("SIGTERM: %s\n", + sigismember(&mask, SIGTERM) ? "blocked" : "not blocked"); +} + +int main(int argc, char **argv) +{ + pid_t pid; + + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(EXIT_FAILURE); + } else if (!pid) { + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_handler = handler; + act.sa_flags = SA_NODEFER; + + sigaddset(&act.sa_mask, SIGUSR2); + sigaddset(&act.sa_mask, SIGTERM); + + sigaction(SIGUSR1, &act, NULL); + + pause(); + } else { + int status; + + sleep(3); + kill(pid, SIGUSR1); + waitpid(pid, &status, 0); + } + + return 0; +} diff --git a/usr/klibc/tests/sigint.c b/usr/klibc/tests/sigint.c new file mode 100644 index 0000000..ec62e33 --- /dev/null +++ b/usr/klibc/tests/sigint.c @@ -0,0 +1,64 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +static sig_atomic_t counter = 0; + +static void sig_handler(int signum) +{ + static const char msg[] = "Signal handler\n"; + + (void)signum; + + write(1, msg, sizeof msg - 1); + counter++; +} + +int main(int argc, char *argv[]) +{ + struct sigaction act, oact; + pid_t f; + sigset_t set; + + (void)argc; + + memset(&act, 0x00, sizeof(struct sigaction)); + act.sa_handler = sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + + /* oact is there for the benefit of strace() */ + sigaction(SIGINT, &act, &oact); + sigaction(SIGTERM, &act, &oact); + + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigprocmask(SIG_BLOCK, &set, NULL); + + f = fork(); + + if (f < 0) { + perror(argv[0]); + exit(255); + } else if (f > 0) { + sleep(3); + if (counter) { + fprintf(stderr, "Signal received while masked!\n"); + exit(1); + } + sigprocmask(SIG_UNBLOCK, &set, NULL); + sleep(3); + if (!counter) { + fprintf(stderr, "No signal received!\n"); + exit(1); + } else { + printf("Signal received OK\n"); + exit(0); + } + } else { + sleep(1); + kill(getppid(), SIGINT); + _exit(0); + } +} diff --git a/usr/klibc/tests/socket.c b/usr/klibc/tests/socket.c new file mode 100644 index 0000000..48814f9 --- /dev/null +++ b/usr/klibc/tests/socket.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/socket.h> + +int main(int argc, char *argv[]) +{ + int ret; + + ret = socket(AF_INET, SOCK_DGRAM, 0); + if (ret == -1) { + fprintf(stderr, "klibc: socket(AF_INET): %s\n", + strerror(errno)); + return 1; + } + + return 0; +} diff --git a/usr/klibc/tests/sscanf.c b/usr/klibc/tests/sscanf.c new file mode 100644 index 0000000..c06aae4 --- /dev/null +++ b/usr/klibc/tests/sscanf.c @@ -0,0 +1,36 @@ +#include <stdio.h> + +int main() +{ + int ret, err = 0, e1, e2; + const char a1[] = "3.0", a2[] = "-12,1000"; + + /* int tests */ + ret = sscanf(a1, "%1d", &e1); + if (ret != 1) { + printf("Error wrong sscanf int return %d.\n", ret); + err++; + } + if (e1 != 3) { + printf("Error wrong sscanf int reading %d.\n", e1); + err++; + } + ret = sscanf(a2, "%3d,%4d", &e1, &e2); + if (ret != 2) { + printf("Error wrong sscanf int return %d.\n", ret); + err++; + } + if (e1 != -12) { + printf("Error wrong sscanf int reading %d.\n", e1); + err++; + } + if (e2 != 1000) { + printf("Error wrong sscanf int reading %d.\n", e2); + err++; + } + /* XXX: add more int tests */ + + if (err) + return err; + return 0; +} diff --git a/usr/klibc/tests/stat.c b/usr/klibc/tests/stat.c new file mode 100644 index 0000000..4410833 --- /dev/null +++ b/usr/klibc/tests/stat.c @@ -0,0 +1,62 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/types.h> + +static void do_stat(const char *path) +{ + struct stat st; + + if (stat(path, &st)) { + perror(path); + exit(1); + } + + printf("Path = %s\n" + " st_dev = %#jx (%u,%u)\n" + " st_ino = %ju\n" + " st_mode = %#jo\n" + " st_nlink = %ju\n" + " st_uid = %ju\n" + " st_gid = %ju\n" + " st_rdev = %#jx (%u,%u)\n" + " st_size = %ju\n" + " st_blksize = %ju\n" + " st_blocks = %ju\n", + path, + (uintmax_t) st.st_dev, major(st.st_dev), minor(st.st_dev), + (uintmax_t) st.st_ino, + (uintmax_t) st.st_mode, + (uintmax_t) st.st_nlink, + (uintmax_t) st.st_uid, + (uintmax_t) st.st_gid, + (uintmax_t) st.st_rdev, major(st.st_rdev), minor(st.st_rdev), + (uintmax_t) st.st_size, + (uintmax_t) st.st_blksize, (uintmax_t) st.st_blocks); + +#ifdef _STATBUF_ST_NSEC + printf(" st_atim = %jd.%09u\n" + " st.mtim = %jd.%09u\n" + " st.ctim = %jd.%09u\n", + (uintmax_t) st.st_atim.tv_sec, (unsigned int)st.st_atim.tv_nsec, + (uintmax_t) st.st_mtim.tv_sec, (unsigned int)st.st_mtim.tv_nsec, + (uintmax_t) st.st_ctim.tv_sec, (unsigned int)st.st_ctim.tv_nsec); +#else + printf(" st_atime = %jd\n" + " st.mtime = %jd\n" + " st.ctime = %jd\n", + (uintmax_t) st.st_atime, + (uintmax_t) st.st_mtime, (uintmax_t) st.st_ctime); +#endif +} + +int main(int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) + do_stat(argv[i]); + + return 0; +} diff --git a/usr/klibc/tests/statfs.c b/usr/klibc/tests/statfs.c new file mode 100644 index 0000000..0ac8051 --- /dev/null +++ b/usr/klibc/tests/statfs.c @@ -0,0 +1,41 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/vfs.h> + +static void do_statfs(const char *path) +{ + struct statfs sfs; + + if (statfs(path, &sfs)) { + perror(path); + exit(1); + } + + printf("Path = %s\n" + " f_type = %#jx\n" + " f_bsize = %jd\n" + " f_blocks = %jd\n" + " f_bfree = %jd\n" + " f_bavail = %jd\n" + " f_files = %jd\n" + " f_ffree = %jd\n" + " f_namelen = %jd\n", + path, + (uintmax_t) sfs.f_type, + (intmax_t) sfs.f_bsize, + (intmax_t) sfs.f_blocks, + (intmax_t) sfs.f_bfree, + (intmax_t) sfs.f_bavail, + (intmax_t) sfs.f_files, + (intmax_t) sfs.f_ffree, (intmax_t) sfs.f_namelen); +} + +int main(int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) + do_statfs(argv[i]); + + return 0; +} diff --git a/usr/klibc/tests/stdio.c b/usr/klibc/tests/stdio.c new file mode 100644 index 0000000..895c730 --- /dev/null +++ b/usr/klibc/tests/stdio.c @@ -0,0 +1,70 @@ +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/mman.h> +#include <arpa/inet.h> +#include <inttypes.h> + +#define TEST_WORDS (1024*1024) + +int main(void) +{ + FILE *f; + int i, n; + uint32_t *buf1, *buf2, *p; + + printf("Hello, World!\nHello again"); + printf(" - and some more - "); + printf("and some more\n"); + + buf1 = mmap(NULL, 2*4*TEST_WORDS, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); + if (buf1 == MAP_FAILED) { + perror("mmap"); + return 1; + } + + buf2 = buf1 + TEST_WORDS; + + p = buf1; + for (i = 0; i < TEST_WORDS; i++) + *p++ = htonl(i); + + f = fopen("test.out", "w+b"); + if (!f) { + perror("fopen"); + return 1; + } + + for (i = 2; i < TEST_WORDS; i += (i >> 1)) { + n = fwrite(buf1, 4, i, f); + if (n != i) { + perror("fwrite"); + return 1; + } + } + + fprintf(f, "Writing to the file...\n"); + fprintf(f, "Writing to the file "); + fprintf(f, "some more\n"); + + fseek(f, 0, SEEK_SET); + + for (i = 2; i < TEST_WORDS; i += (i >> 1)) { + n = fread(buf2, 4, i, f); + if (n != i) { + perror("fread"); + return 1; + } + + if (memcmp(buf1, buf2, i*4)) { + fprintf(stderr, "memory mismatch error, i = %d\n", i); + return 1; + } + } + + fclose(f); + return 0; +} diff --git a/usr/klibc/tests/strlcpycat.c b/usr/klibc/tests/strlcpycat.c new file mode 100644 index 0000000..14df0cd --- /dev/null +++ b/usr/klibc/tests/strlcpycat.c @@ -0,0 +1,111 @@ +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +int main(void) +{ + char temp[8]; + size_t len; + + printf("strlcpy:\n"); + len = strlcpy(temp, "123", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "123") != 0) + goto error; + + len = strlcpy(temp, "", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "") != 0) + goto error; + + len = strlcpy(temp, "1234567890", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "1234567") != 0) + goto error; + + len = strlcpy(temp, "123", 1); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "") != 0) + goto error; + + len = strlcpy(temp, "1234567890", 1); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "") != 0) + goto error; + + len = strlcpy(temp, "123", 0); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "") != 0) + goto error; + + len = strlcpy(temp, "1234567890", 0); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "") != 0) + goto error; + + len = strlcpy(temp, "1234567", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "1234567") != 0) + goto error; + + len = strlcpy(temp, "12345678", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "1234567") != 0) + goto error; + + printf("\n"); + printf("strlcat:\n"); + strcpy(temp, ""); + len = strlcat(temp, "123", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "123") != 0) + goto error; + + strcpy(temp, "ABC"); + len = strlcat(temp, "", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "ABC") != 0) + goto error; + + strcpy(temp, ""); + len = strlcat(temp, "", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "") != 0) + goto error; + + strcpy(temp, "ABC"); + len = strlcat(temp, "123", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "ABC123") != 0) + goto error; + + strcpy(temp, "ABC"); + len = strlcat(temp, "1234567890", sizeof(temp)); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "ABC1234") != 0) + goto error; + + strcpy(temp, "ABC"); + len = strlcat(temp, "123", 5); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "ABC1") != 0) + goto error; + + strcpy(temp, "ABC"); + len = strlcat(temp, "123", 1); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "ABC") != 0) + goto error; + + strcpy(temp, "ABC"); + len = strlcat(temp, "123", 0); + printf("'%s'len:%zu strlen:%zu\n", temp, len, strlen(temp)); + if (strcmp(temp, "ABC") != 0) + goto error; + + exit(0); +error: + printf("unexpected result\n"); + exit(1); +} diff --git a/usr/klibc/tests/strsearch.c b/usr/klibc/tests/strsearch.c new file mode 100644 index 0000000..01fd55d --- /dev/null +++ b/usr/klibc/tests/strsearch.c @@ -0,0 +1,113 @@ +#include <stdio.h> +#include <string.h> + +int main(void) +{ + const char haystack[] = "haystack"; + const char empty[] = ""; + const char *p; + size_t len; + + p = strchr(haystack, 'a'); + printf("found 'a' at offset %zd\n", p ? p - haystack : -1); + if (p != haystack + 1) + goto error; + p = strchr(haystack, 'b'); + printf("found 'b' at offset %zd\n", p ? p - haystack : -1); + if (p != NULL) + goto error; + p = strchr(haystack, 0); + printf("found 0 at offset %zd\n", p ? p - haystack : -1); + if (p != haystack + 8) + goto error; + + p = strrchr(haystack, 'a'); + printf("found 'a' at offset %zd\n", p ? p - haystack : -1); + if (p != haystack + 5) + goto error; + p = strrchr(haystack, 'b'); + printf("found 'b' at offset %zd\n", p ? p - haystack : -1); + if (p != NULL) + goto error; + p = strrchr(haystack, 0); + printf("found 0 at offset %zd\n", p ? p - haystack : -1); + if (p != haystack + 8) + goto error; + + len = strspn(haystack, "hasty"); + printf("found %zu chars from 'hasty'\n", len); + if (len != 6) + goto error; + len = strspn(haystack, "haystack"); + printf("found %zu chars from 'haystack'\n", len); + if (len != 8) + goto error; + len = strspn(haystack, ""); + printf("found %zu chars from ''\n", len); + if (len != 0) + goto error; + + len = strcspn(haystack, "stick"); + printf("found %zu chars not from 'stick'\n", len); + if (len != 3) + goto error; + len = strcspn(haystack, "needle"); + printf("found %zu chars not from 'needle'\n", len); + if (len != 8) + goto error; + len = strcspn(haystack, ""); + printf("found %zu chars not from ''\n", len); + if (len != 8) + goto error; + + p = strpbrk(haystack, "stick"); + printf("found char from 'stick' at offset %zd\n", p ? p - haystack : -1); + if (p != haystack + 3) + goto error; + p = strpbrk(haystack, "needle"); + printf("found char from 'needle' at offset %zd\n", p ? p - haystack : -1); + if (p != NULL) + goto error; + p = strpbrk(haystack, ""); + printf("found char from '' at offset %zd\n", p ? p - haystack : -1); + if (p != NULL) + goto error; + + p = strstr(haystack, "stack"); + printf("found 'stack' at offset %zd\n", p ? p - haystack : -1); + if (p != haystack + 3) + goto error; + p = strstr(haystack, "tacks"); + printf("found 'tacks' at offset %zd\n", p ? p - haystack : -1); + if (p != NULL) + goto error; + p = strstr(haystack, "needle"); + printf("found 'needle' at offset %zd\n", p ? p - haystack : -1); + if (p != NULL) + goto error; + p = strstr(haystack, "k"); + printf("found 'k' at offset %zd\n", p ? p - haystack : -1); + if (p != haystack + 7) + goto error; + p = strstr(haystack, "b"); + printf("found 'b' at offset %zd\n", p ? p - haystack : -1); + if (p != NULL) + goto error; + p = strstr(haystack, "kk"); + printf("found 'kk' at offset %zd\n", p ? p - haystack : -1); + if (p != NULL) + goto error; + p = strstr(haystack, ""); + printf("found '' at offset %zd\n", p ? p - haystack : -1); + if (p != haystack) + goto error; + p = strstr(empty, ""); + printf("found '' at offset %zd\n", p ? p - empty : -1); + if (p != empty) + goto error; + + return 0; +error: + printf("unexpected result\n"); + return 1; +} diff --git a/usr/klibc/tests/strtoimax.c b/usr/klibc/tests/strtoimax.c new file mode 100644 index 0000000..318f2d4 --- /dev/null +++ b/usr/klibc/tests/strtoimax.c @@ -0,0 +1,23 @@ +/* + * strtoimaxtest.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> + +int main(int argc, char *argv[]) +{ + int i; + char *ep; + intmax_t iv; + + for (i = 1; i < argc; i++) { + iv = strtoimax(argv[i], &ep, 0); + printf("strtoimax(\"%s\") = %jd\n", argv[i], iv); + if (*ep) + printf(" ep = \"%s\"\n", ep); + } + + return 0; +} diff --git a/usr/klibc/tests/strtotime.c b/usr/klibc/tests/strtotime.c new file mode 100644 index 0000000..16d2e0f --- /dev/null +++ b/usr/klibc/tests/strtotime.c @@ -0,0 +1,25 @@ +#include <stdio.h> +#include <time.h> + +int main(int argc, char *argv[]) +{ + struct timeval tv; + struct timespec ts; + int i; + const char *rv, *rs; + + for (i = 1; i < argc; i++) { + rs = strtotimespec(argv[i], &ts); + rv = strtotimeval(argv[i], &tv); + printf("String: \"%s\"\n" + "Timespec: %ld.%09ld\n" + "Residual: \"%s\"\n" + "Timeval: %ld.%06ld\n" + "Residual: \"%s\"\n", + argv[i], + (long)ts.tv_sec, (long)ts.tv_nsec, rs, + (long)tv.tv_sec, (long)tv.tv_usec, rv); + } + + return 0; +} diff --git a/usr/klibc/tests/sysconf.c b/usr/klibc/tests/sysconf.c new file mode 100644 index 0000000..e32c138 --- /dev/null +++ b/usr/klibc/tests/sysconf.c @@ -0,0 +1,14 @@ +#include <assert.h> +#include <stdio.h> +#include <sys/sysconf.h> + +int main(void) +{ + long rc; + + rc = sysconf(_SC_PAGESIZE); + assert(rc > 0); + printf("sysconf(_SC_PAGESIZE) = %ld\n", rc); + + return 0; +} diff --git a/usr/klibc/tests/testrand48.c b/usr/klibc/tests/testrand48.c new file mode 100644 index 0000000..26868c7 --- /dev/null +++ b/usr/klibc/tests/testrand48.c @@ -0,0 +1,19 @@ +#include <stdlib.h> +#include <stdio.h> + +int main(void) +{ + unsigned short seed1[] = { 0x1234, 0x5678, 0x9abc }; + unsigned short *oldseed; + + oldseed = seed48(seed1); + printf("Initial seed: %#06x %#06x %#06x\n", + oldseed[0], oldseed[1], oldseed[2]); + + printf("lrand48() = %ld\n", lrand48()); + + seed48(seed1); + printf("mrand48() = %ld\n", mrand48()); + + return 1; +} diff --git a/usr/klibc/tests/testvsnp.c b/usr/klibc/tests/testvsnp.c new file mode 100644 index 0000000..7120bda --- /dev/null +++ b/usr/klibc/tests/testvsnp.c @@ -0,0 +1,118 @@ +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +int main(void) +{ + int r, i; + char buffer[512]; + + r = snprintf(buffer, 512, "Hello, %d", 37); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'d", 37373737); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'x", 0xdeadbeef); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'#X", 0xdeadbeef); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'#llo", 0123456701234567ULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + /* Make sure overflow works correctly */ + memset(buffer, '\xff', 512); + r = snprintf(buffer, 16, "Hello, %'#llo", 0123456701234567ULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + for (i = 16; i < 512; i++) + assert(buffer[i] == '\xff'); + + r = snprintf(buffer, 512, "Hello, %'#40.20llo", 0123456701234567ULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'#-40.20llo", 0123456701234567ULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'#*.*llo", 40, 20, + 0123456701234567ULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'#*.*llo", -40, 20, + 0123456701234567ULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'#*.*llo", -40, -20, + 0123456701234567ULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %'#*.*llx", -40, -20, + 0123456701234567ULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %p", &buffer); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %P", &buffer); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %20p", &buffer); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %-20p", &buffer); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 512, "Hello, %-20p", NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 20, "Hello, %'-20p", NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 15, "Hello, %'-20p", NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 3, "Hello, %'-20p", NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + /* This shouldn't change buffer in any way! */ + r = snprintf(buffer, 0, "Hello, %'-20p", NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + for (i = -30; i <= 30; i++) { + r = snprintf(buffer, 40, "Hello, %'*p", i, NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), + r); + } + + r = snprintf(buffer, 40, "Hello, %'-20s", "String"); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 40, "Hello, %'20s", "String"); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 40, "Hello, %'020s", "String"); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 40, "Hello, %'-20s", NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 40, "Hello, %'20s", NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 40, "Hello, %'020s", NULL); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 40, "Hello, %'-20c", '*'); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 40, "Hello, %'20c", '*'); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + r = snprintf(buffer, 40, "Hello, %'020c", '*'); + printf("buffer = \"%s\" (%d), r = %d\n", buffer, strlen(buffer), r); + + return 0; +} diff --git a/usr/klibc/tests/vfork.c b/usr/klibc/tests/vfork.c new file mode 100644 index 0000000..7adc036 --- /dev/null +++ b/usr/klibc/tests/vfork.c @@ -0,0 +1,45 @@ +/* + * usr/klibc/tests/vfork.c + * + * vfork is messy on most architectures. Do our best to test it out. + */ + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +int main(int argc, char *argv[]) +{ + pid_t f, rv; + int status; + + f = vfork(); + + if (f == 0) { + printf("Child (%d)...\n", (int)getpid()); + _exit(123); + } else if (f > 0) { + int err = 0; + + printf("Parent (child = %d)\n", (int)f); + + rv = waitpid(f, &status, 0); + if (rv != f) { + printf("waitpid returned %d, errno = %d\n", + (int)rv, errno); + err++; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 123) { + printf("Child process existed with wrong status %d\n", + status); + err++; + } + return err; + } else { + printf("vfork returned %d, errno = %d\n", + (int)f, errno); + return 127; + } +} diff --git a/usr/klibc/time.c b/usr/klibc/time.c new file mode 100644 index 0000000..e812931 --- /dev/null +++ b/usr/klibc/time.c @@ -0,0 +1,19 @@ +/* + * time.c + */ + +#include <time.h> +#include <sys/time.h> +#include <sys/syscall.h> + +time_t time(time_t * t) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + if (t) + *t = (time_t) tv.tv_sec; + + return (time_t) tv.tv_sec; +} diff --git a/usr/klibc/umount.c b/usr/klibc/umount.c new file mode 100644 index 0000000..d31a300 --- /dev/null +++ b/usr/klibc/umount.c @@ -0,0 +1,12 @@ +/* + * umount.c + * + * Single-argument form of umount + */ + +#include <sys/mount.h> + +int umount(const char *dir) +{ + return umount2(dir, 0); +} diff --git a/usr/klibc/unlink.c b/usr/klibc/unlink.c new file mode 100644 index 0000000..6dfe66c --- /dev/null +++ b/usr/klibc/unlink.c @@ -0,0 +1,12 @@ +#include <fcntl.h> +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef __NR_unlink + +int unlink(const char *pathname) +{ + return unlinkat(AT_FDCWD, pathname, 0); +} + +#endif /* __NR_unlink */ diff --git a/usr/klibc/unsetenv.c b/usr/klibc/unsetenv.c new file mode 100644 index 0000000..c5cc95a --- /dev/null +++ b/usr/klibc/unsetenv.c @@ -0,0 +1,44 @@ +/* + * unsetenv.c + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "env.h" + +int unsetenv(const char *name) +{ + size_t len; + char **p, *q; + const char *z; + + if (!name || !name[0]) { + errno = EINVAL; + return -1; + } + + len = 0; + for (z = name; *z; z++) { + len++; + if (*z == '=') { + errno = EINVAL; + return -1; + } + } + + if (!environ) + return 0; + + for (p = environ; (q = *p); p++) { + if (!strncmp(name, q, len) && q[len] == '=') + break; + } + + for (; (q = *p); p++) { + p[0] = p[1]; + } + + return 0; +} diff --git a/usr/klibc/userdb/getgrgid.c b/usr/klibc/userdb/getgrgid.c new file mode 100644 index 0000000..549aec3 --- /dev/null +++ b/usr/klibc/userdb/getgrgid.c @@ -0,0 +1,18 @@ +/* + * getgrgid.c + * + * Dummy getgrgid() to support udev + */ + +#include <grp.h> + +#include "userdb.h" + +struct group *getgrgid(gid_t gid) +{ + if (!gid) + return (struct group *)&__root_group; + + errno = ENOENT; + return NULL; +} diff --git a/usr/klibc/userdb/getgrnam.c b/usr/klibc/userdb/getgrnam.c new file mode 100644 index 0000000..ac184bc --- /dev/null +++ b/usr/klibc/userdb/getgrnam.c @@ -0,0 +1,18 @@ +/* + * getgrnam.c + * + * Dummy getgrnam() to support udev + */ + +#include <grp.h> + +#include "userdb.h" + +struct group *getgrnam(const char *name) +{ + if (!strcmp(name, "root")) + return (struct group *)&__root_group; + + errno = ENOENT; + return NULL; +} diff --git a/usr/klibc/userdb/getpwnam.c b/usr/klibc/userdb/getpwnam.c new file mode 100644 index 0000000..3a95688 --- /dev/null +++ b/usr/klibc/userdb/getpwnam.c @@ -0,0 +1,19 @@ +/* + * getpwnam.c + * + * Dummy getpwnam() to support udev + */ + +#include <pwd.h> + +#include "userdb.h" + + +struct passwd *getpwnam(const char *name) +{ + if (!strcmp(name, "root")) + return (struct passwd *)&__root_user; + + errno = ENOENT; + return NULL; +} diff --git a/usr/klibc/userdb/getpwuid.c b/usr/klibc/userdb/getpwuid.c new file mode 100644 index 0000000..cbe706a --- /dev/null +++ b/usr/klibc/userdb/getpwuid.c @@ -0,0 +1,19 @@ +/* + * getpwuid.c + * + * Dummy getpwuid() to support udev + */ + +#include <pwd.h> + +#include "userdb.h" + + +struct passwd *getpwuid(uid_t uid) +{ + if (!uid) + return (struct passwd *)&__root_user; + + errno = ENOENT; + return NULL; +} diff --git a/usr/klibc/userdb/root_group.c b/usr/klibc/userdb/root_group.c new file mode 100644 index 0000000..e936d54 --- /dev/null +++ b/usr/klibc/userdb/root_group.c @@ -0,0 +1,12 @@ +/* + * root_group.c + */ + +#include "userdb.h" + +const struct group __root_group = { + .gr_name = "root", + .gr_passwd = "", + .gr_gid = 0, + .gr_mem = NULL +}; diff --git a/usr/klibc/userdb/root_user.c b/usr/klibc/userdb/root_user.c new file mode 100644 index 0000000..ee2e99a --- /dev/null +++ b/usr/klibc/userdb/root_user.c @@ -0,0 +1,17 @@ +/* + * root_user.c + * + */ + +#include "userdb.h" +#include <paths.h> + +const struct passwd __root_user = { + .pw_name = "root", + .pw_passwd = "", + .pw_uid = 0, + .pw_gid = 0, + .pw_gecos = "root", + .pw_dir = "/", + .pw_shell = _PATH_BSHELL +}; diff --git a/usr/klibc/userdb/userdb.h b/usr/klibc/userdb/userdb.h new file mode 100644 index 0000000..dda093f --- /dev/null +++ b/usr/klibc/userdb/userdb.h @@ -0,0 +1,19 @@ +/* + * userdb.h + * + * Common header file + */ + +#ifndef USERDB_H +#define USERDB_H + +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <string.h> +#include <errno.h> + +extern const struct passwd __root_user; +extern const struct group __root_group; + +#endif /* USERDB_H */ diff --git a/usr/klibc/usleep.c b/usr/klibc/usleep.c new file mode 100644 index 0000000..67b1533 --- /dev/null +++ b/usr/klibc/usleep.c @@ -0,0 +1,17 @@ +/* + * usleep.c + */ + +#include <errno.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +void usleep(unsigned long usec) +{ + struct timespec ts; + + ts.tv_sec = usec / 1000000UL; + ts.tv_nsec = (usec % 1000000UL) * 1000; + while (nanosleep(&ts, &ts) == -1 && errno == EINTR) ; +} diff --git a/usr/klibc/utime.c b/usr/klibc/utime.c new file mode 100644 index 0000000..bfae2d9 --- /dev/null +++ b/usr/klibc/utime.c @@ -0,0 +1,20 @@ +/* + * utime.c + */ + +#include <utime.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/syscall.h> + +int utime(const char *filename, const struct utimbuf *buf) +{ + struct timeval tvp[2]; + + tvp[0].tv_sec = buf->actime; + tvp[0].tv_usec = 0; + tvp[1].tv_sec = buf->modtime; + tvp[1].tv_usec = 0; + + return utimes(filename, tvp); +} diff --git a/usr/klibc/utimes.c b/usr/klibc/utimes.c new file mode 100644 index 0000000..74cb822 --- /dev/null +++ b/usr/klibc/utimes.c @@ -0,0 +1,18 @@ +#include <fcntl.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/syscall.h> + +int utimes(const char *file, const struct timeval tvp[2]) +{ + struct timespec ts[2]; + + if (tvp) { + ts[0].tv_sec = tvp[0].tv_sec; + ts[0].tv_nsec = tvp[0].tv_usec * 1000; + ts[1].tv_sec = tvp[1].tv_sec; + ts[1].tv_nsec = tvp[1].tv_usec * 1000; + } + + return utimensat(AT_FDCWD, file, &ts[0], 0); +} diff --git a/usr/klibc/vasprintf.c b/usr/klibc/vasprintf.c new file mode 100644 index 0000000..cdc302f --- /dev/null +++ b/usr/klibc/vasprintf.c @@ -0,0 +1,25 @@ +/* + * vasprintf.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +int vasprintf(char **bufp, const char *format, va_list ap) +{ + va_list ap1; + int bytes; + char *p; + + va_copy(ap1, ap); + + bytes = vsnprintf(NULL, 0, format, ap1) + 1; + va_end(ap1); + + *bufp = p = malloc(bytes); + if (!p) + return -1; + + return vsnprintf(p, bytes, format, ap); +} diff --git a/usr/klibc/version b/usr/klibc/version new file mode 100644 index 0000000..82bd22f --- /dev/null +++ b/usr/klibc/version @@ -0,0 +1 @@ +2.0.13 diff --git a/usr/klibc/vfork.c b/usr/klibc/vfork.c new file mode 100644 index 0000000..f2b1a85 --- /dev/null +++ b/usr/klibc/vfork.c @@ -0,0 +1,16 @@ +/* + * vfork.c + * + * Emulate vfork() with fork() if necessary + */ + +#include <unistd.h> +#include <klibc/compiler.h> +#include <klibc/sysconfig.h> + +#if !_KLIBC_NO_MMU && !_KLIBC_REAL_VFORK +int vfork(void) +{ + return fork(); +} +#endif diff --git a/usr/klibc/vfprintf.c b/usr/klibc/vfprintf.c new file mode 100644 index 0000000..6f5663b --- /dev/null +++ b/usr/klibc/vfprintf.c @@ -0,0 +1,26 @@ +/* + * vfprintf.c + */ + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> + +#define BUFFER_SIZE 32768 + +int vfprintf(FILE * file, const char *format, va_list ap) +{ + int rv; + char buffer[BUFFER_SIZE]; + + rv = vsnprintf(buffer, BUFFER_SIZE, format, ap); + + if (rv < 0) + return rv; + + if (rv > BUFFER_SIZE - 1) + rv = BUFFER_SIZE - 1; + + return _fwrite(buffer, rv, file); +} diff --git a/usr/klibc/vprintf.c b/usr/klibc/vprintf.c new file mode 100644 index 0000000..d6bfeaf --- /dev/null +++ b/usr/klibc/vprintf.c @@ -0,0 +1,11 @@ +/* + * vprintf.c + */ + +#include <stdio.h> +#include <stdarg.h> + +int vprintf(const char *format, va_list ap) +{ + return vfprintf(stdout, format, ap); +} diff --git a/usr/klibc/vsnprintf.c b/usr/klibc/vsnprintf.c new file mode 100644 index 0000000..26be710 --- /dev/null +++ b/usr/klibc/vsnprintf.c @@ -0,0 +1,488 @@ +/* + * vsnprintf.c + * + * vsnprintf(), from which the rest of the printf() + * family is built + */ + +#include <stdarg.h> +#include <stddef.h> +#include <inttypes.h> +#include <string.h> +#include <limits.h> +#include <stdio.h> + +enum flags { + FL_ZERO = 0x01, /* Zero modifier */ + FL_MINUS = 0x02, /* Minus modifier */ + FL_PLUS = 0x04, /* Plus modifier */ + FL_TICK = 0x08, /* ' modifier */ + FL_SPACE = 0x10, /* Space modifier */ + FL_HASH = 0x20, /* # modifier */ + FL_SIGNED = 0x40, /* Number is signed */ + FL_UPPER = 0x80 /* Upper case digits */ +}; + +/* These may have to be adjusted on certain implementations */ +enum ranks { + rank_char = -2, + rank_short = -1, + rank_int = 0, + rank_long = 1, + rank_longlong = 2 +}; + +#define MIN_RANK rank_char +#define MAX_RANK rank_longlong + +#define INTMAX_RANK rank_longlong +#define SIZE_T_RANK rank_long +#define PTRDIFF_T_RANK rank_long + +#define EMIT(x) ({ if (o<n){*q++ = (x);} o++; }) + +static size_t +format_int(char *q, size_t n, uintmax_t val, enum flags flags, + int base, int width, int prec) +{ + char *qq; + size_t o = 0, oo; + static const char lcdigits[] = "0123456789abcdef"; + static const char ucdigits[] = "0123456789ABCDEF"; + const char *digits; + uintmax_t tmpval; + int minus = 0; + int ndigits = 0, nchars; + int tickskip, b4tick; + + /* Select type of digits */ + digits = (flags & FL_UPPER) ? ucdigits : lcdigits; + + /* If signed, separate out the minus */ + if (flags & FL_SIGNED && (intmax_t) val < 0) { + minus = 1; + val = (uintmax_t) (-(intmax_t) val); + } + + /* Count the number of digits needed. This returns zero for 0. */ + tmpval = val; + while (tmpval) { + tmpval /= base; + ndigits++; + } + + /* Adjust ndigits for size of output */ + + if (flags & FL_HASH && base == 8) { + if (prec < ndigits + 1) + prec = ndigits + 1; + } + + if (ndigits < prec) { + ndigits = prec; /* Mandatory number padding */ + } else if (val == 0) { + ndigits = 1; /* Zero still requires space */ + } + + /* For ', figure out what the skip should be */ + if (flags & FL_TICK) { + tickskip = (base == 16) ? 4 : 3; + } else { + tickskip = ndigits; /* No tick marks */ + } + + /* Tick marks aren't digits, but generated by the number converter */ + ndigits += (ndigits - 1) / tickskip; + + /* Now compute the number of nondigits */ + nchars = ndigits; + + if (minus || (flags & (FL_PLUS | FL_SPACE))) + nchars++; /* Need space for sign */ + if ((flags & FL_HASH) && base == 16) { + nchars += 2; /* Add 0x for hex */ + } + + /* Emit early space padding */ + if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars) { + while (width > nchars) { + EMIT(' '); + width--; + } + } + + /* Emit nondigits */ + if (minus) + EMIT('-'); + else if (flags & FL_PLUS) + EMIT('+'); + else if (flags & FL_SPACE) + EMIT(' '); + + if ((flags & FL_HASH) && base == 16) { + EMIT('0'); + EMIT((flags & FL_UPPER) ? 'X' : 'x'); + } + + /* Emit zero padding */ + if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits) { + while (width > nchars) { + EMIT('0'); + width--; + } + } + + /* Generate the number. This is done from right to left. */ + q += ndigits; /* Advance the pointer to end of number */ + o += ndigits; + qq = q; + oo = o; /* Temporary values */ + + b4tick = tickskip; + while (ndigits > 0) { + if (!b4tick--) { + qq--; + oo--; + ndigits--; + if (oo < n) + *qq = '_'; + b4tick = tickskip - 1; + } + qq--; + oo--; + ndigits--; + if (oo < n) + *qq = digits[val % base]; + val /= base; + } + + /* Emit late space padding */ + while ((flags & FL_MINUS) && width > nchars) { + EMIT(' '); + width--; + } + + return o; +} + +int vsnprintf(char *buffer, size_t n, const char *format, va_list ap) +{ + const char *p = format; + char ch; + char *q = buffer; + size_t o = 0; /* Number of characters output */ + uintmax_t val = 0; + int rank = rank_int; /* Default rank */ + int width = 0; + int prec = -1; + int base; + size_t sz; + enum flags flags = 0; + enum { + st_normal, /* Ground state */ + st_flags, /* Special flags */ + st_width, /* Field width */ + st_prec, /* Field precision */ + st_modifiers /* Length or conversion modifiers */ + } state = st_normal; + const char *sarg; /* %s string argument */ + char carg; /* %c char argument */ + int slen; /* String length */ + + while ((ch = *p++)) { + switch (state) { + case st_normal: + if (ch == '%') { + state = st_flags; + flags = 0; + rank = rank_int; + width = 0; + prec = -1; + } else { + EMIT(ch); + } + break; + + case st_flags: + switch (ch) { + case '-': + flags |= FL_MINUS; + break; + case '+': + flags |= FL_PLUS; + break; + case '\'': + flags |= FL_TICK; + break; + case ' ': + flags |= FL_SPACE; + break; + case '#': + flags |= FL_HASH; + break; + case '0': + flags |= FL_ZERO; + break; + default: + state = st_width; + p--; /* Process this character again */ + break; + } + break; + + case st_width: + if (ch >= '0' && ch <= '9') { + width = width * 10 + (ch - '0'); + } else if (ch == '*') { + width = va_arg(ap, int); + if (width < 0) { + width = -width; + flags |= FL_MINUS; + } + } else if (ch == '.') { + prec = 0; /* Precision given */ + state = st_prec; + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_prec: + if (ch >= '0' && ch <= '9') { + prec = prec * 10 + (ch - '0'); + } else if (ch == '*') { + prec = va_arg(ap, int); + if (prec < 0) + prec = -1; + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_modifiers: + switch (ch) { + /* Length modifiers - nonterminal sequences */ + case 'h': + rank--; /* Shorter rank */ + break; + case 'l': + rank++; /* Longer rank */ + break; + case 'j': + rank = INTMAX_RANK; + break; + case 'z': + rank = SIZE_T_RANK; + break; + case 't': + rank = PTRDIFF_T_RANK; + break; + case 'L': + case 'q': + rank += 2; + break; + default: + /* Output modifiers - terminal sequences */ + + /* Next state will be normal */ + state = st_normal; + + /* Canonicalize rank */ + if (rank < MIN_RANK) + rank = MIN_RANK; + else if (rank > MAX_RANK) + rank = MAX_RANK; + + switch (ch) { + case 'P': /* Upper case pointer */ + flags |= FL_UPPER; + /* fall through */ + case 'p': /* Pointer */ + base = 16; + prec = (CHAR_BIT*sizeof(void *)+3)/4; + flags |= FL_HASH; + val = (uintmax_t)(uintptr_t) + va_arg(ap, void *); + goto is_integer; + + case 'd': /* Signed decimal output */ + case 'i': + base = 10; + flags |= FL_SIGNED; + switch (rank) { + case rank_char: + /* Yes, all these casts are + needed... */ + val = (uintmax_t)(intmax_t) + (signed char) + va_arg(ap, signed int); + break; + case rank_short: + val = (uintmax_t)(intmax_t) + (signed short) + va_arg(ap, signed int); + break; + case rank_int: + val = (uintmax_t)(intmax_t) + va_arg(ap, signed int); + break; + case rank_long: + val = (uintmax_t)(intmax_t) + va_arg(ap, signed long); + break; + case rank_longlong: + val = (uintmax_t)(intmax_t) + va_arg(ap, + signed long long); + break; + } + goto is_integer; + case 'o': /* Octal */ + base = 8; + goto is_unsigned; + case 'u': /* Unsigned decimal */ + base = 10; + goto is_unsigned; + case 'X': /* Upper case hexadecimal */ + flags |= FL_UPPER; + /* fall through */ + case 'x': /* Hexadecimal */ + base = 16; + goto is_unsigned; + + is_unsigned: + switch (rank) { + case rank_char: + val = (uintmax_t) + (unsigned char) + va_arg(ap, unsigned + int); + break; + case rank_short: + val = (uintmax_t) + (unsigned short) + va_arg(ap, unsigned + int); + break; + case rank_int: + val = (uintmax_t) + va_arg(ap, unsigned + int); + break; + case rank_long: + val = (uintmax_t) + va_arg(ap, unsigned + long); + break; + case rank_longlong: + val = (uintmax_t) + va_arg(ap, unsigned + long long); + break; + } + /* fall through */ + + is_integer: + sz = format_int(q, (o < n) ? n - o : 0, + val, flags, base, + width, prec); + q += sz; + o += sz; + break; + + case 'c': /* Character */ + carg = (char)va_arg(ap, int); + sarg = &carg; + slen = 1; + goto is_string; + case 's': /* String */ + sarg = va_arg(ap, const char *); + sarg = sarg ? sarg : "(null)"; + slen = strlen(sarg); + goto is_string; + + is_string: + { + char sch; + int i; + + if (prec != -1 && slen > prec) + slen = prec; + + if (width > slen + && !(flags & FL_MINUS)) { + char pad = + (flags & FL_ZERO) ? + '0' : ' '; + while (width > slen) { + EMIT(pad); + width--; + } + } + for (i = slen; i; i--) { + sch = *sarg++; + EMIT(sch); + } + if (width > slen + && (flags & FL_MINUS)) { + while (width > slen) { + EMIT(' '); + width--; + } + } + } + break; + + case 'n': + { + /* Output the number of + characters written */ + + switch (rank) { + case rank_char: + *va_arg(ap, + signed char *) + = o; + break; + case rank_short: + *va_arg(ap, + signed short *) + = o; + break; + case rank_int: + *va_arg(ap, + signed int *) + = o; + break; + case rank_long: + *va_arg(ap, + signed long *) + = o; + break; + case rank_longlong: + *va_arg(ap, + signed long long *) + = o; + break; + } + } + break; + + default: /* Anything else, including % */ + EMIT(ch); + break; + } + } + } + } + + /* Null-terminate the string */ + if (o < n) + *q = '\0'; /* No overflow */ + else if (n > 0) + buffer[n - 1] = '\0'; /* Overflow - terminate at end of buffer */ + + return o; +} diff --git a/usr/klibc/vsprintf.c b/usr/klibc/vsprintf.c new file mode 100644 index 0000000..51f5d87 --- /dev/null +++ b/usr/klibc/vsprintf.c @@ -0,0 +1,11 @@ +/* + * vsprintf.c + */ + +#include <stdio.h> +#include <unistd.h> + +int vsprintf(char *buffer, const char *format, va_list ap) +{ + return vsnprintf(buffer, ~(size_t) 0, format, ap); +} diff --git a/usr/klibc/vsscanf.c b/usr/klibc/vsscanf.c new file mode 100644 index 0000000..b8f068c --- /dev/null +++ b/usr/klibc/vsscanf.c @@ -0,0 +1,392 @@ +/* + * vsscanf.c + * + * vsscanf(), from which the rest of the scanf() + * family is built + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stddef.h> +#include <inttypes.h> +#include <string.h> +#include <limits.h> +#include <stdio.h> + +#ifndef LONG_BIT +#define LONG_BIT (CHAR_BIT*sizeof(long)) +#endif + +enum flags { + FL_SPLAT = 0x01, /* Drop the value, do not assign */ + FL_INV = 0x02, /* Character-set with inverse */ + FL_WIDTH = 0x04, /* Field width specified */ + FL_MINUS = 0x08, /* Negative number */ +}; + +enum ranks { + rank_char = -2, + rank_short = -1, + rank_int = 0, + rank_long = 1, + rank_longlong = 2, + rank_ptr = INT_MAX /* Special value used for pointers */ +}; + +#define MIN_RANK rank_char +#define MAX_RANK rank_longlong + +#define INTMAX_RANK rank_longlong +#define SIZE_T_RANK rank_long +#define PTRDIFF_T_RANK rank_long + +enum bail { + bail_none = 0, /* No error condition */ + bail_eof, /* Hit EOF */ + bail_err /* Conversion mismatch */ +}; + +static inline const char *skipspace(const char *p) +{ + while (isspace((unsigned char)*p)) + p++; + return p; +} + +#undef set_bit +static inline void set_bit(unsigned long *bitmap, unsigned int bit) +{ + bitmap[bit / LONG_BIT] |= 1UL << (bit % LONG_BIT); +} + +#undef test_bit +static inline int test_bit(unsigned long *bitmap, unsigned int bit) +{ + return (int)(bitmap[bit / LONG_BIT] >> (bit % LONG_BIT)) & 1; +} + +int vsscanf(const char *buffer, const char *format, va_list ap) +{ + const char *p = format; + char ch; + unsigned char uc; + const char *q = buffer; + const char *qq; + uintmax_t val = 0; + int rank = rank_int; /* Default rank */ + unsigned int width = UINT_MAX; + int base; + enum flags flags = 0; + enum { + st_normal, /* Ground state */ + st_flags, /* Special flags */ + st_width, /* Field width */ + st_modifiers, /* Length or conversion modifiers */ + st_match_init, /* Initial state of %[ sequence */ + st_match, /* Main state of %[ sequence */ + st_match_range, /* After - in a %[ sequence */ + } state = st_normal; + char *sarg = NULL; /* %s %c or %[ string argument */ + enum bail bail = bail_none; + int converted = 0; /* Successful conversions */ + unsigned long matchmap[((1 << CHAR_BIT) + (LONG_BIT - 1)) / LONG_BIT]; + int matchinv = 0; /* Is match map inverted? */ + unsigned char range_start = 0; + + while ((ch = *p++) && !bail) { + switch (state) { + case st_normal: + if (ch == '%') { + state = st_flags; + flags = 0; + rank = rank_int; + width = UINT_MAX; + } else if (isspace((unsigned char)ch)) { + q = skipspace(q); + } else { + if (*q == ch) + q++; + else + bail = bail_err; /* Match failure */ + } + break; + + case st_flags: + switch (ch) { + case '*': + flags |= FL_SPLAT; + break; + case '0'...'9': + width = (ch - '0'); + state = st_width; + flags |= FL_WIDTH; + break; + default: + state = st_modifiers; + p--; /* Process this character again */ + break; + } + break; + + case st_width: + if (ch >= '0' && ch <= '9') { + width = width * 10 + (ch - '0'); + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_modifiers: + switch (ch) { + /* Length modifiers - nonterminal sequences */ + case 'h': + rank--; /* Shorter rank */ + break; + case 'l': + rank++; /* Longer rank */ + break; + case 'j': + rank = INTMAX_RANK; + break; + case 'z': + rank = SIZE_T_RANK; + break; + case 't': + rank = PTRDIFF_T_RANK; + break; + case 'L': + case 'q': + rank = rank_longlong; /* long double/long long */ + break; + + default: + /* Output modifiers - terminal sequences */ + /* Next state will be normal */ + state = st_normal; + + /* Canonicalize rank */ + if (rank < MIN_RANK) + rank = MIN_RANK; + else if (rank > MAX_RANK) + rank = MAX_RANK; + + switch (ch) { + case 'P': /* Upper case pointer */ + case 'p': /* Pointer */ + rank = rank_ptr; + base = 0; + goto scan_int; + + case 'i': /* Base-independent integer */ + base = 0; + goto scan_int; + + case 'd': /* Decimal integer */ + base = 10; + goto scan_int; + + case 'o': /* Octal integer */ + base = 8; + goto scan_int; + + case 'u': /* Unsigned decimal integer */ + base = 10; + goto scan_int; + + case 'x': /* Hexadecimal integer */ + case 'X': + base = 16; + goto scan_int; + + case 'n': /* # of characters consumed */ + val = (q - buffer); + goto set_integer; + + scan_int: + q = skipspace(q); + if (!*q) { + bail = bail_eof; + break; + } + val = + strntoumax(q, (char **)&qq, base, + width); + if (qq == q) { + bail = bail_err; + break; + } + q = qq; + if (!(flags & FL_SPLAT)) + converted++; + /* fall through */ + + set_integer: + if (!(flags & FL_SPLAT)) { + switch (rank) { + case rank_char: + *va_arg(ap, + unsigned char *) + = val; + break; + case rank_short: + *va_arg(ap, + unsigned short + *) = val; + break; + case rank_int: + *va_arg(ap, + unsigned int *) + = val; + break; + case rank_long: + *va_arg(ap, + unsigned long *) + = val; + break; + case rank_longlong: + *va_arg(ap, + unsigned long + long *) = val; + break; + case rank_ptr: + *va_arg(ap, void **) = + (void *) + (uintptr_t)val; + break; + } + } + break; + + case 'c': /* Character */ + /* Default width == 1 */ + width = (flags & FL_WIDTH) ? width : 1; + if (flags & FL_SPLAT) { + while (width--) { + if (!*q) { + bail = bail_eof; + break; + } + } + } else { + sarg = va_arg(ap, char *); + while (width--) { + if (!*q) { + bail = bail_eof; + break; + } + *sarg++ = *q++; + } + if (!bail) + converted++; + } + break; + + case 's': /* String */ + uc = 1; /* Anything nonzero */ + if (flags & FL_SPLAT) { + while (width-- && (uc = *q) && + !isspace(uc)) { + q++; + } + } else { + char *sp; + sp = sarg = va_arg(ap, char *); + while (width-- && (uc = *q) && + !isspace(uc)) { + *sp++ = uc; + q++; + } + if (sarg != sp) { + /* Terminate output */ + *sp = '\0'; + converted++; + } + } + if (!uc) + bail = bail_eof; + break; + + case '[': /* Character range */ + sarg = (flags & FL_SPLAT) ? NULL + : va_arg(ap, char *); + state = st_match_init; + matchinv = 0; + memset(matchmap, 0, sizeof matchmap); + break; + + case '%': /* %% sequence */ + if (*q == '%') + q++; + else + bail = bail_err; + break; + + default: /* Anything else */ + /* Unknown sequence */ + bail = bail_err; + break; + } + } + break; + + case st_match_init: /* Initial state for %[ match */ + if (ch == '^' && !(flags & FL_INV)) { + matchinv = 1; + } else { + set_bit(matchmap, (unsigned char)ch); + state = st_match; + } + break; + + case st_match: /* Main state for %[ match */ + if (ch == ']') { + goto match_run; + } else if (ch == '-') { + range_start = (unsigned char)ch; + state = st_match_range; + } else { + set_bit(matchmap, (unsigned char)ch); + } + break; + + case st_match_range: /* %[ match after - */ + if (ch == ']') { + /* - was last character */ + set_bit(matchmap, (unsigned char)'-'); + goto match_run; + } else { + int i; + for (i = range_start; i < (unsigned char)ch; + i++) + set_bit(matchmap, i); + state = st_match; + } + break; + + match_run: /* Match expression finished */ + qq = q; + uc = 1; /* Anything nonzero */ + while (width && (uc = *q) + && test_bit(matchmap, uc)^matchinv) { + if (sarg) + *sarg++ = uc; + q++; + } + if (q != qq && sarg) { + *sarg = '\0'; + converted++; + } else { + bail = bail_err; + } + if (!uc) + bail = bail_eof; + break; + } + } + + if (bail == bail_eof && !converted) + converted = -1; /* Return EOF (-1) */ + + return converted; +} diff --git a/usr/klibc/wait.c b/usr/klibc/wait.c new file mode 100644 index 0000000..73bb1a2 --- /dev/null +++ b/usr/klibc/wait.c @@ -0,0 +1,12 @@ +/* + * wait.c + */ + +#include <stdlib.h> +#include <sys/wait.h> +#include <sys/types.h> + +pid_t wait(int *status) +{ + return wait4((pid_t) - 1, status, 0, NULL); +} diff --git a/usr/klibc/wait3.c b/usr/klibc/wait3.c new file mode 100644 index 0000000..1f35414 --- /dev/null +++ b/usr/klibc/wait3.c @@ -0,0 +1,12 @@ +/* + * wait3.c + */ + +#include <sys/types.h> +#include <sys/resource.h> +#include <sys/wait.h> + +pid_t wait3(int *status, int options, struct rusage * rusage) +{ + return wait4((pid_t) - 1, status, options, rusage); +} diff --git a/usr/klibc/waitpid.c b/usr/klibc/waitpid.c new file mode 100644 index 0000000..a157122 --- /dev/null +++ b/usr/klibc/waitpid.c @@ -0,0 +1,12 @@ +/* + * waitpid.c + */ + +#include <sys/types.h> +#include <sys/resource.h> +#include <sys/wait.h> + +pid_t waitpid(pid_t pid, int *status, int options) +{ + return wait4(pid, status, options, NULL); +} diff --git a/usr/klibc/zalloc.c b/usr/klibc/zalloc.c new file mode 100644 index 0000000..0a5e823 --- /dev/null +++ b/usr/klibc/zalloc.c @@ -0,0 +1,17 @@ +/* + * zalloc.c + */ + +#include <stdlib.h> +#include <string.h> + +void *zalloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + if (ptr) + memset(ptr, 0, size); + + return ptr; +} diff --git a/usr/klibc/zlib/FAQ b/usr/klibc/zlib/FAQ new file mode 100644 index 0000000..441d910 --- /dev/null +++ b/usr/klibc/zlib/FAQ @@ -0,0 +1,339 @@ + + Frequently Asked Questions about zlib + + +If your question is not there, please check the zlib home page +http://www.zlib.org which may have more recent information. +The lastest zlib FAQ is at http://www.gzip.org/zlib/zlib_faq.html + + + 1. Is zlib Y2K-compliant? + + Yes. zlib doesn't handle dates. + + 2. Where can I get a Windows DLL version? + + The zlib sources can be compiled without change to produce a DLL. + See the file win32/DLL_FAQ.txt in the zlib distribution. + Pointers to the precompiled DLL are found in the zlib web site at + http://www.zlib.org. + + 3. Where can I get a Visual Basic interface to zlib? + + See + * http://www.dogma.net/markn/articles/zlibtool/zlibtool.htm + * contrib/visual-basic.txt in the zlib distribution + * win32/DLL_FAQ.txt in the zlib distribution + + 4. compress() returns Z_BUF_ERROR. + + Make sure that before the call of compress, the length of the compressed + buffer is equal to the total size of the compressed buffer and not + zero. For Visual Basic, check that this parameter is passed by reference + ("as any"), not by value ("as long"). + + 5. deflate() or inflate() returns Z_BUF_ERROR. + + Before making the call, make sure that avail_in and avail_out are not + zero. When setting the parameter flush equal to Z_FINISH, also make sure + that avail_out is big enough to allow processing all pending input. + Note that a Z_BUF_ERROR is not fatal--another call to deflate() or + inflate() can be made with more input or output space. A Z_BUF_ERROR + may in fact be unavoidable depending on how the functions are used, since + it is not possible to tell whether or not there is more output pending + when strm.avail_out returns with zero. + + 6. Where's the zlib documentation (man pages, etc.)? + + It's in zlib.h for the moment, and Francis S. Lin has converted it to a + web page zlib.html. Volunteers to transform this to Unix-style man pages, + please contact us (zlib@gzip.org). Examples of zlib usage are in the files + example.c and minigzip.c. + + 7. Why don't you use GNU autoconf or libtool or ...? + + Because we would like to keep zlib as a very small and simple + package. zlib is rather portable and doesn't need much configuration. + + 8. I found a bug in zlib. + + Most of the time, such problems are due to an incorrect usage of + zlib. Please try to reproduce the problem with a small program and send + the corresponding source to us at zlib@gzip.org . Do not send + multi-megabyte data files without prior agreement. + + 9. Why do I get "undefined reference to gzputc"? + + If "make test" produces something like + + example.o(.text+0x154): undefined reference to `gzputc' + + check that you don't have old files libz.* in /usr/lib, /usr/local/lib or + /usr/X11R6/lib. Remove any old versions, then do "make install". + +10. I need a Delphi interface to zlib. + + See the contrib/delphi directory in the zlib distribution. + +11. Can zlib handle .zip archives? + + Not by itself, no. See the directory contrib/minizip in the zlib + distribution. + +12. Can zlib handle .Z files? + + No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt + the code of uncompress on your own. + +13. How can I make a Unix shared library? + + make clean + ./configure -s + make + +14. How do I install a shared zlib library on Unix? + + After the above, then: + + make install + + However, many flavors of Unix come with a shared zlib already installed. + Before going to the trouble of compiling a shared version of zlib and + trying to install it, you may want to check if it's already there! If you + can #include <zlib.h>, it's there. The -lz option will probably link to it. + +15. I have a question about OttoPDF. + + We are not the authors of OttoPDF. The real author is on the OttoPDF web + site: Joel Hainley, jhainley@myndkryme.com. + +16. Can zlib decode Flate data in an Adobe PDF file? + + Yes. See http://www.fastio.com/ (ClibPDF), or http://www.pdflib.com/ . + To modify PDF forms, see http://sourceforge.net/projects/acroformtool/ . + +17. Why am I getting this "register_frame_info not found" error on Solaris? + + After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib + generates an error such as: + + ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so: + symbol __register_frame_info: referenced symbol not found + + The symbol __register_frame_info is not part of zlib, it is generated by + the C compiler (cc or gcc). You must recompile applications using zlib + which have this problem. This problem is specific to Solaris. See + http://www.sunfreeware.com for Solaris versions of zlib and applications + using zlib. + +18. Why does gzip give an error on a file I make with compress/deflate? + + The compress and deflate functions produce data in the zlib format, which + is different and incompatible with the gzip format. The gz* functions in + zlib on the other hand use the gzip format. Both the zlib and gzip + formats use the same compressed data format internally, but have different + headers and trailers around the compressed data. + +19. Ok, so why are there two different formats? + + The gzip format was designed to retain the directory information about + a single file, such as the name and last modification date. The zlib + format on the other hand was designed for in-memory and communication + channel applications, and has a much more compact header and trailer and + uses a faster integrity check than gzip. + +20. Well that's nice, but how do I make a gzip file in memory? + + You can request that deflate write the gzip format instead of the zlib + format using deflateInit2(). You can also request that inflate decode + the gzip format using inflateInit2(). Read zlib.h for more details. + +21. Is zlib thread-safe? + + Yes. However any library routines that zlib uses and any application- + provided memory allocation routines must also be thread-safe. zlib's gz* + functions use stdio library routines, and most of zlib's functions use the + library memory allocation routines by default. zlib's Init functions allow + for the application to provide custom memory allocation routines. + + Of course, you should only operate on any given zlib or gzip stream from a + single thread at a time. + +22. Can I use zlib in my commercial application? + + Yes. Please read the license in zlib.h. + +23. Is zlib under the GNU license? + + No. Please read the license in zlib.h. + +24. The license says that altered source versions must be "plainly marked". So + what exactly do I need to do to meet that requirement? + + You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In + particular, the final version number needs to be changed to "f", and an + identification string should be appended to ZLIB_VERSION. Version numbers + x.x.x.f are reserved for modifications to zlib by others than the zlib + maintainers. For example, if the version of the base zlib you are altering + is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and + ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also + update the version strings in deflate.c and inftrees.c. + + For altered source distributions, you should also note the origin and + nature of the changes in zlib.h, as well as in ChangeLog and README, along + with the dates of the alterations. The origin should include at least your + name (or your company's name), and an email address to contact for help or + issues with the library. + + Note that distributing a compiled zlib library along with zlib.h and + zconf.h is also a source distribution, and so you should change + ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes + in zlib.h as you would for a full source distribution. + +25. Will zlib work on a big-endian or little-endian architecture, and can I + exchange compressed data between them? + + Yes and yes. + +26. Will zlib work on a 64-bit machine? + + It should. It has been tested on 64-bit machines, and has no dependence + on any data types being limited to 32-bits in length. If you have any + difficulties, please provide a complete problem report to zlib@gzip.org + +27. Will zlib decompress data from the PKWare Data Compression Library? + + No. The PKWare DCL uses a completely different compressed data format + than does PKZIP and zlib. However, you can look in zlib's contrib/blast + directory for a possible solution to your problem. + +28. Can I access data randomly in a compressed stream? + + No, not without some preparation. If when compressing you periodically + use Z_FULL_FLUSH, carefully write all the pending data at those points, + and keep an index of those locations, then you can start decompression + at those points. You have to be careful to not use Z_FULL_FLUSH too + often, since it can significantly degrade compression. + +29. Does zlib work on MVS, OS/390, CICS, etc.? + + We don't know for sure. We have heard occasional reports of success on + these systems. If you do use it on one of these, please provide us with + a report, instructions, and patches that we can reference when we get + these questions. Thanks. + +30. Is there some simpler, easier to read version of inflate I can look at + to understand the deflate format? + + First off, you should read RFC 1951. Second, yes. Look in zlib's + contrib/puff directory. + +31. Does zlib infringe on any patents? + + As far as we know, no. In fact, that was originally the whole point behind + zlib. Look here for some more information: + + http://www.gzip.org/#faq11 + +32. Can zlib work with greater than 4 GB of data? + + Yes. inflate() and deflate() will process any amount of data correctly. + Each call of inflate() or deflate() is limited to input and output chunks + of the maximum value that can be stored in the compiler's "unsigned int" + type, but there is no limit to the number of chunks. Note however that the + strm.total_in and strm_total_out counters may be limited to 4 GB. These + counters are provided as a convenience and are not used internally by + inflate() or deflate(). The application can easily set up its own counters + updated after each call of inflate() or deflate() to count beyond 4 GB. + compress() and uncompress() may be limited to 4 GB, since they operate in a + single call. gzseek() and gztell() may be limited to 4 GB depending on how + zlib is compiled. See the zlibCompileFlags() function in zlib.h. + + The word "may" appears several times above since there is a 4 GB limit + only if the compiler's "long" type is 32 bits. If the compiler's "long" + type is 64 bits, then the limit is 16 exabytes. + +33. Does zlib have any security vulnerabilities? + + The only one that we are aware of is potentially in gzprintf(). If zlib + is compiled to use sprintf() or vsprintf(), then there is no protection + against a buffer overflow of a 4K string space, other than the caller of + gzprintf() assuring that the output will not exceed 4K. On the other + hand, if zlib is compiled to use snprintf() or vsnprintf(), which should + normally be the case, then there is no vulnerability. The ./configure + script will display warnings if an insecure variation of sprintf() will + be used by gzprintf(). Also the zlibCompileFlags() function will return + information on what variant of sprintf() is used by gzprintf(). + + If you don't have snprintf() or vsnprintf() and would like one, you can + find a portable implementation here: + + http://www.ijs.si/software/snprintf/ + + Note that you should be using the most recent version of zlib. Versions + 1.1.3 and before were subject to a double-free vulnerability. + +34. Is there a Java version of zlib? + + Probably what you want is to use zlib in Java. zlib is already included + as part of the Java SDK in the java.util.zip package. If you really want + a version of zlib written in the Java language, look on the zlib home + page for links: http://www.zlib.org/ + +35. I get this or that compiler or source-code scanner warning when I crank it + up to maximally-pedantic. Can't you guys write proper code? + + Many years ago, we gave up attempting to avoid warnings on every compiler + in the universe. It just got to be a waste of time, and some compilers + were downright silly. So now, we simply make sure that the code always + works. + +36. Valgrind (or some similar memory access checker) says that deflate is + performing a conditional jump that depends on an uninitialized value. + Isn't that a bug? + + No. That is intentional for performance reasons, and the output of + deflate is not affected. This only started showing up recently since + zlib 1.2.x uses malloc() by default for allocations, whereas earlier + versions used calloc(), which zeros out the allocated memory. + +37. Will zlib read the (insert any ancient or arcane format here) compressed + data format? + + Probably not. Look in the comp.compression FAQ for pointers to various + formats and associated software. + +38. How can I encrypt/decrypt zip files with zlib? + + zlib doesn't support encryption. The original PKZIP encryption is very weak + and can be broken with freely available programs. To get strong encryption, + use GnuPG, http://www.gnupg.org/ , which already includes zlib compression. + For PKZIP compatible "encryption", look at http://www.info-zip.org/ + +39. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings? + + "gzip" is the gzip format, and "deflate" is the zlib format. They should + probably have called the second one "zlib" instead to avoid confusion + with the raw deflate compressed data format. While the HTTP 1.1 RFC 2616 + correctly points to the zlib specification in RFC 1950 for the "deflate" + transfer encoding, there have been reports of servers and browsers that + incorrectly produce or expect raw deflate data per the deflate + specficiation in RFC 1951, most notably Microsoft. So even though the + "deflate" transfer encoding using the zlib format would be the more + efficient approach (and in fact exactly what the zlib format was designed + for), using the "gzip" transfer encoding is probably more reliable due to + an unfortunate choice of name on the part of the HTTP 1.1 authors. + + Bottom line: use the gzip format for HTTP 1.1 encoding. + +40. Does zlib support the new "Deflate64" format introduced by PKWare? + + No. PKWare has apparently decided to keep that format proprietary, since + they have not documented it as they have previous compression formats. + In any case, the compression improvements are so modest compared to other + more modern approaches, that it's not worth the effort to implement. + +41. Can you please sign these lengthy legal documents and fax them back to us + so that we can use your software in our product? + + No. Go away. Shoo. diff --git a/usr/klibc/zlib/INDEX b/usr/klibc/zlib/INDEX new file mode 100644 index 0000000..0587e59 --- /dev/null +++ b/usr/klibc/zlib/INDEX @@ -0,0 +1,51 @@ +ChangeLog history of changes +FAQ Frequently Asked Questions about zlib +INDEX this file +Makefile makefile for Unix (generated by configure) +Makefile.in makefile for Unix (template for configure) +README guess what +algorithm.txt description of the (de)compression algorithm +configure configure script for Unix +zconf.in.h template for zconf.h (used by configure) + +amiga/ makefiles for Amiga SAS C +as400/ makefiles for IBM AS/400 +msdos/ makefiles for MSDOS +old/ makefiles for various architectures and zlib documentation + files that have not yet been updated for zlib 1.2.x +projects/ projects for various Integrated Development Environments +qnx/ makefiles for QNX +win32/ makefiles for Windows + + zlib public header files (must be kept): +zconf.h +zlib.h + + private source files used to build the zlib library: +adler32.c +compress.c +crc32.c +crc32.h +deflate.c +deflate.h +gzio.c +infback.c +inffast.c +inffast.h +inffixed.h +inflate.c +inflate.h +inftrees.c +inftrees.h +trees.c +trees.h +uncompr.c +zutil.c +zutil.h + + source files for sample programs: +example.c +minigzip.c + + unsupported contribution by third parties +See contrib/README.contrib diff --git a/usr/klibc/zlib/Kbuild b/usr/klibc/zlib/Kbuild new file mode 100644 index 0000000..8bff3d5 --- /dev/null +++ b/usr/klibc/zlib/Kbuild @@ -0,0 +1,12 @@ +# zlib + +klib-y := adler32.o compress.o crc32.o gzio.o +klib-y += uncompr.o deflate.o trees.o zutil.o +klib-y += inflate.o infback.o inftrees.o inffast.o + +# zlib specific flag +EXTRA_KLIBCCFLAGS := -DDYNAMIC_CRC_TABLE + +# Suppress implicit-fallthrough warning for state machine cases +# where this is intentional +EXTRA_KLIBCCFLAGS += $(call cc-disable-warning,implicit-fallthrough) diff --git a/usr/klibc/zlib/README b/usr/klibc/zlib/README new file mode 100644 index 0000000..758cc50 --- /dev/null +++ b/usr/klibc/zlib/README @@ -0,0 +1,125 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.3 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) +and rfc1952.txt (gzip format). These documents are also available in other +formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file example.c which also tests that the library +is working correctly. Another example is given in the file minigzip.c. The +compression library itself is composed of all source files except example.c and +minigzip.c. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile. In short "make test; make install" should work for most +machines. For Unix: "./configure; make test; make install". For MSDOS, use one +of the special makefiles such as Makefile.msc. For VMS, use make_vms.com. + +Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant +<info@winimage.com> for the Windows DLL version. The zlib home page is +http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem, +please check this site to verify that you have the latest version of zlib; +otherwise get the latest version and check whether the problem still exists or +not. + +PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking +for help. + +Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://dogma.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.2.3 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit +http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html +See the zlib home page http://www.zlib.org for details. + +A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is in the +CPAN (Comprehensive Perl Archive Network) sites +http://www.cpan.org/modules/by-module/Compress/ + +A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is +available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com> is +availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant <info@winimage.com>, is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + +- When building a shared, i.e. dynamic library on Mac OS X, the library must be + installed before testing (do "make install" before "make test"), since the + library location is specified in the library. + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-2004 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. Please +read the FAQ for more information on the distribution of modified source +versions. diff --git a/usr/klibc/zlib/adler32.c b/usr/klibc/zlib/adler32.c new file mode 100644 index 0000000..22e1151 --- /dev/null +++ b/usr/klibc/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: adler32.c,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/usr/klibc/zlib/algorithm.txt b/usr/klibc/zlib/algorithm.txt new file mode 100644 index 0000000..b022dde --- /dev/null +++ b/usr/klibc/zlib/algorithm.txt @@ -0,0 +1,209 @@ +1. Compression algorithm (deflate) + +The deflation algorithm used by gzip (also zip and zlib) is a variation of +LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in +the input data. The second occurrence of a string is replaced by a +pointer to the previous string, in the form of a pair (distance, +length). Distances are limited to 32K bytes, and lengths are limited +to 258 bytes. When a string does not occur anywhere in the previous +32K bytes, it is emitted as a sequence of literal bytes. (In this +description, `string' must be taken as an arbitrary sequence of bytes, +and is not restricted to printable characters.) + +Literals or match lengths are compressed with one Huffman tree, and +match distances are compressed with another tree. The trees are stored +in a compact form at the start of each block. The blocks can have any +size (except that the compressed data for one block must fit in +available memory). A block is terminated when deflate() determines that +it would be useful to start another block with fresh trees. (This is +somewhat similar to the behavior of LZW-based _compress_.) + +Duplicated strings are found using a hash table. All input strings of +length 3 are inserted in the hash table. A hash index is computed for +the next 3 bytes. If the hash chain for this index is not empty, all +strings in the chain are compared with the current input string, and +the longest match is selected. + +The hash chains are searched starting with the most recent strings, to +favor small distances and thus take advantage of the Huffman encoding. +The hash chains are singly linked. There are no deletions from the +hash chains, the algorithm simply discards matches that are too old. + +To avoid a worst-case situation, very long hash chains are arbitrarily +truncated at a certain length, determined by a runtime option (level +parameter of deflateInit). So deflate() does not always find the longest +possible match but generally finds a match which is long enough. + +deflate() also defers the selection of matches with a lazy evaluation +mechanism. After a match of length N has been found, deflate() searches for +a longer match at the next input byte. If a longer match is found, the +previous match is truncated to a length of one (thus producing a single +literal byte) and the process of lazy evaluation begins again. Otherwise, +the original match is kept, and the next match search is attempted only N +steps later. + +The lazy match evaluation is also subject to a runtime parameter. If +the current match is long enough, deflate() reduces the search for a longer +match, thus speeding up the whole process. If compression ratio is more +important than speed, deflate() attempts a complete second search even if +the first match is already long enough. + +The lazy match evaluation is not performed for the fastest compression +modes (level parameter 1 to 3). For these fast modes, new strings +are inserted in the hash table only when no match was found, or +when the match is not too long. This degrades the compression ratio +but saves time since there are both fewer insertions and fewer searches. + + +2. Decompression algorithm (inflate) + +2.1 Introduction + +The key question is how to represent a Huffman code (or any prefix code) so +that you can decode fast. The most important characteristic is that shorter +codes are much more common than longer codes, so pay attention to decoding the +short codes fast, and let the long codes take longer to decode. + +inflate() sets up a first level table that covers some number of bits of +input less than the length of longest code. It gets that many bits from the +stream, and looks it up in the table. The table will tell if the next +code is that many bits or less and how many, and if it is, it will tell +the value, else it will point to the next level table for which inflate() +grabs more bits and tries to decode a longer code. + +How many bits to make the first lookup is a tradeoff between the time it +takes to decode and the time it takes to build the table. If building the +table took no time (and if you had infinite memory), then there would only +be a first level table to cover all the way to the longest code. However, +building the table ends up taking a lot longer for more bits since short +codes are replicated many times in such a table. What inflate() does is +simply to make the number of bits in the first table a variable, and then +to set that variable for the maximum speed. + +For inflate, which has 286 possible codes for the literal/length tree, the size +of the first table is nine bits. Also the distance trees have 30 possible +values, and the size of the first table is six bits. Note that for each of +those cases, the table ended up one bit longer than the ``average'' code +length, i.e. the code length of an approximately flat code which would be a +little more than eight bits for 286 symbols and a little less than five bits +for 30 symbols. + + +2.2 More details on the inflate table lookup + +Ok, you want to know what this cleverly obfuscated inflate tree actually +looks like. You are correct that it's not a Huffman tree. It is simply a +lookup table for the first, let's say, nine bits of a Huffman symbol. The +symbol could be as short as one bit or as long as 15 bits. If a particular +symbol is shorter than nine bits, then that symbol's translation is duplicated +in all those entries that start with that symbol's bits. For example, if the +symbol is four bits, then it's duplicated 32 times in a nine-bit table. If a +symbol is nine bits long, it appears in the table once. + +If the symbol is longer than nine bits, then that entry in the table points +to another similar table for the remaining bits. Again, there are duplicated +entries as needed. The idea is that most of the time the symbol will be short +and there will only be one table look up. (That's whole idea behind data +compression in the first place.) For the less frequent long symbols, there +will be two lookups. If you had a compression method with really long +symbols, you could have as many levels of lookups as is efficient. For +inflate, two is enough. + +So a table entry either points to another table (in which case nine bits in +the above example are gobbled), or it contains the translation for the symbol +and the number of bits to gobble. Then you start again with the next +ungobbled bit. + +You may wonder: why not just have one lookup table for how ever many bits the +longest symbol is? The reason is that if you do that, you end up spending +more time filling in duplicate symbol entries than you do actually decoding. +At least for deflate's output that generates new trees every several 10's of +kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code +would take too long if you're only decoding several thousand symbols. At the +other extreme, you could make a new table for every bit in the code. In fact, +that's essentially a Huffman tree. But then you spend two much time +traversing the tree while decoding, even for short symbols. + +So the number of bits for the first lookup table is a trade of the time to +fill out the table vs. the time spent looking at the second level and above of +the table. + +Here is an example, scaled down: + +The code being decoded, with 10 symbols, from 1 to 6 bits long: + +A: 0 +B: 10 +C: 1100 +D: 11010 +E: 11011 +F: 11100 +G: 11101 +H: 11110 +I: 111110 +J: 111111 + +Let's make the first table three bits long (eight entries): + +000: A,1 +001: A,1 +010: A,1 +011: A,1 +100: B,2 +101: B,2 +110: -> table X (gobble 3 bits) +111: -> table Y (gobble 3 bits) + +Each entry is what the bits decode as and how many bits that is, i.e. how +many bits to gobble. Or the entry points to another table, with the number of +bits to gobble implicit in the size of the table. + +Table X is two bits long since the longest code starting with 110 is five bits +long: + +00: C,1 +01: C,1 +10: D,2 +11: E,2 + +Table Y is three bits long since the longest code starting with 111 is six +bits long: + +000: F,2 +001: F,2 +010: G,2 +011: G,2 +100: H,2 +101: H,2 +110: I,3 +111: J,3 + +So what we have here are three tables with a total of 20 entries that had to +be constructed. That's compared to 64 entries for a single table. Or +compared to 16 entries for a Huffman tree (six two entry tables and one four +entry table). Assuming that the code ideally represents the probability of +the symbols, it takes on the average 1.25 lookups per symbol. That's compared +to one lookup for the single table, or 1.66 lookups per symbol for the +Huffman tree. + +There, I think that gives you a picture of what's going on. For inflate, the +meaning of a particular symbol is often more than just a letter. It can be a +byte (a "literal"), or it can be either a length or a distance which +indicates a base value and a number of bits to fetch after the code that is +added to the base value. Or it might be the special end-of-block code. The +data structures created in inftrees.c try to encode all that information +compactly in the tables. + + +Jean-loup Gailly Mark Adler +jloup@gzip.org madler@alumni.caltech.edu + + +References: + +[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data +Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3, +pp. 337-343. + +``DEFLATE Compressed Data Format Specification'' available in +http://www.ietf.org/rfc/rfc1951.txt diff --git a/usr/klibc/zlib/compress.c b/usr/klibc/zlib/compress.c new file mode 100644 index 0000000..4f48f55 --- /dev/null +++ b/usr/klibc/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: compress.c,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/usr/klibc/zlib/crc32.c b/usr/klibc/zlib/crc32.c new file mode 100644 index 0000000..f658a9e --- /dev/null +++ b/usr/klibc/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include <stdio.h> +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include <limits.h> +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/usr/klibc/zlib/crc32.h b/usr/klibc/zlib/crc32.h new file mode 100644 index 0000000..8053b61 --- /dev/null +++ b/usr/klibc/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/usr/klibc/zlib/deflate.c b/usr/klibc/zlib/deflate.c new file mode 100644 index 0000000..7a9b03d --- /dev/null +++ b/usr/klibc/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id: deflate.c,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/usr/klibc/zlib/deflate.h b/usr/klibc/zlib/deflate.h new file mode 100644 index 0000000..0a37d70 --- /dev/null +++ b/usr/klibc/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: deflate.h,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/usr/klibc/zlib/gzio.c b/usr/klibc/zlib/gzio.c new file mode 100644 index 0000000..0116ea3 --- /dev/null +++ b/usr/klibc/zlib/gzio.c @@ -0,0 +1,1029 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id: gzio.c,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#include <stdio.h> + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "<fd:%d>", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + /* klibc hack */ + if (errno) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + /* klibc hack */ + if (len == 0 && errno) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (errno) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include <stdarg.h> + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + /* klibc hack */ + /* clearerr(s->file); */ +} diff --git a/usr/klibc/zlib/infback.c b/usr/klibc/zlib/infback.c new file mode 100644 index 0000000..455dbc9 --- /dev/null +++ b/usr/klibc/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/usr/klibc/zlib/inffast.c b/usr/klibc/zlib/inffast.c new file mode 100644 index 0000000..bbee92e --- /dev/null +++ b/usr/klibc/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/usr/klibc/zlib/inffast.h b/usr/klibc/zlib/inffast.h new file mode 100644 index 0000000..1e88d2d --- /dev/null +++ b/usr/klibc/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/usr/klibc/zlib/inffixed.h b/usr/klibc/zlib/inffixed.h new file mode 100644 index 0000000..75ed4b5 --- /dev/null +++ b/usr/klibc/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/usr/klibc/zlib/inflate.c b/usr/klibc/zlib/inflate.c new file mode 100644 index 0000000..792fdee --- /dev/null +++ b/usr/klibc/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include <stdio.h> + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/usr/klibc/zlib/inflate.h b/usr/klibc/zlib/inflate.h new file mode 100644 index 0000000..07bd3e7 --- /dev/null +++ b/usr/klibc/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/usr/klibc/zlib/inftrees.c b/usr/klibc/zlib/inftrees.c new file mode 100644 index 0000000..8a9c13f --- /dev/null +++ b/usr/klibc/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/usr/klibc/zlib/inftrees.h b/usr/klibc/zlib/inftrees.h new file mode 100644 index 0000000..b1104c8 --- /dev/null +++ b/usr/klibc/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/usr/klibc/zlib/trees.c b/usr/klibc/zlib/trees.c new file mode 100644 index 0000000..82578a1 --- /dev/null +++ b/usr/klibc/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id: trees.c,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include <ctype.h> +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1<<extra_lbits[code]); n++) { + _length_code[length++] = (uch)code; + } + } + Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + _length_code[length-1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + _dist_code[dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include <stdio.h> +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +local void build_tree(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/usr/klibc/zlib/trees.h b/usr/klibc/zlib/trees.h new file mode 100644 index 0000000..aadfa16 --- /dev/null +++ b/usr/klibc/zlib/trees.h @@ -0,0 +1,127 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; diff --git a/usr/klibc/zlib/uncompr.c b/usr/klibc/zlib/uncompr.c new file mode 100644 index 0000000..debee43 --- /dev/null +++ b/usr/klibc/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: uncompr.c,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/usr/klibc/zlib/zconf.in.h b/usr/klibc/zlib/zconf.in.h new file mode 100644 index 0000000..c7575eb --- /dev/null +++ b/usr/klibc/zlib/zconf.in.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.in.h,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include <windows.h> + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include <sys/types.h> /* for off_t */ +# include <unistd.h> /* for SEEK_* and off_t */ +# ifdef VMS +# include <unixio.h> /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/usr/klibc/zlib/zlib.3 b/usr/klibc/zlib/zlib.3 new file mode 100644 index 0000000..90b8162 --- /dev/null +++ b/usr/klibc/zlib/zlib.3 @@ -0,0 +1,159 @@ +.TH ZLIB 3 "18 July 2005" +.SH NAME +zlib \- compression/decompression library +.SH SYNOPSIS +[see +.I zlib.h +for full description] +.SH DESCRIPTION +The +.I zlib +library is a general purpose data compression library. +The code is thread safe. +It provides in-memory compression and decompression functions, +including integrity checks of the uncompressed data. +This version of the library supports only one compression method (deflation) +but other algorithms will be added later +and will have the same stream interface. +.LP +Compression can be done in a single step if the buffers are large enough +(for example if an input file is mmap'ed), +or can be done by repeated calls of the compression function. +In the latter case, +the application must provide more input and/or consume the output +(providing more output space) before each call. +.LP +The library also supports reading and writing files in +.IR gzip (1) +(.gz) format +with an interface similar to that of stdio. +.LP +The library does not install any signal handler. +The decoder checks the consistency of the compressed data, +so the library should never crash even in case of corrupted input. +.LP +All functions of the compression library are documented in the file +.IR zlib.h . +The distribution source includes examples of use of the library +in the files +.I example.c +and +.IR minigzip.c . +.LP +Changes to this version are documented in the file +.I ChangeLog +that accompanies the source, +and are concerned primarily with bug fixes and portability enhancements. +.LP +A Java implementation of +.I zlib +is available in the Java Development Kit 1.1: +.IP +http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html +.LP +A Perl interface to +.IR zlib , +written by Paul Marquess (pmqs@cpan.org), +is available at CPAN (Comprehensive Perl Archive Network) sites, +including: +.IP +http://www.cpan.org/modules/by-module/Compress/ +.LP +A Python interface to +.IR zlib , +written by A.M. Kuchling (amk@magnet.com), +is available in Python 1.5 and later versions: +.IP +http://www.python.org/doc/lib/module-zlib.html +.LP +A +.I zlib +binding for +.IR tcl (1), +written by Andreas Kupries (a.kupries@westend.com), +is availlable at: +.IP +http://www.westend.com/~kupries/doc/trf/man/man.html +.LP +An experimental package to read and write files in .zip format, +written on top of +.I zlib +by Gilles Vollant (info@winimage.com), +is available at: +.IP +http://www.winimage.com/zLibDll/unzip.html +and also in the +.I contrib/minizip +directory of the main +.I zlib +web site. +.SH "SEE ALSO" +The +.I zlib +web site can be found at either of these locations: +.IP +http://www.zlib.org +.br +http://www.gzip.org/zlib/ +.LP +The data format used by the zlib library is described by RFC +(Request for Comments) 1950 to 1952 in the files: +.IP +http://www.ietf.org/rfc/rfc1950.txt (concerning zlib format) +.br +http://www.ietf.org/rfc/rfc1951.txt (concerning deflate format) +.br +http://www.ietf.org/rfc/rfc1952.txt (concerning gzip format) +.LP +These documents are also available in other formats from: +.IP +ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html +.LP +Mark Nelson (markn@ieee.org) wrote an article about +.I zlib +for the Jan. 1997 issue of Dr. Dobb's Journal; +a copy of the article is available at: +.IP +http://dogma.net/markn/articles/zlibtool/zlibtool.htm +.SH "REPORTING PROBLEMS" +Before reporting a problem, +please check the +.I zlib +web site to verify that you have the latest version of +.IR zlib ; +otherwise, +obtain the latest version and see if the problem still exists. +Please read the +.I zlib +FAQ at: +.IP +http://www.gzip.org/zlib/zlib_faq.html +.LP +before asking for help. +Send questions and/or comments to zlib@gzip.org, +or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). +.SH AUTHORS +Version 1.2.3 +Copyright (C) 1995-2005 Jean-loup Gailly (jloup@gzip.org) +and Mark Adler (madler@alumni.caltech.edu). +.LP +This software is provided "as-is," +without any express or implied warranty. +In no event will the authors be held liable for any damages +arising from the use of this software. +See the distribution directory with respect to requirements +governing redistribution. +The deflate format used by +.I zlib +was defined by Phil Katz. +The deflate and +.I zlib +specifications were written by L. Peter Deutsch. +Thanks to all the people who reported problems and suggested various +improvements in +.IR zlib ; +who are too numerous to cite here. +.LP +UNIX manual page by R. P. C. Rodgers, +U.S. National Library of Medicine (rodgers@nlm.nih.gov). +.\" end of man page diff --git a/usr/klibc/zlib/zutil.c b/usr/klibc/zlib/zutil.c new file mode 100644 index 0000000..f9ce953 --- /dev/null +++ b/usr/klibc/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zutil.c,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/usr/klibc/zlib/zutil.h b/usr/klibc/zlib/zutil.h new file mode 100644 index 0000000..42f52b1 --- /dev/null +++ b/usr/klibc/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.1 2005/02/27 23:15:39 hpa Exp $ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include <stddef.h> +# endif +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include <errno.h> +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include <alloc.h> +# endif +# else /* MSC or DJGPP */ +# include <malloc.h> +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include <malloc.h> +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include <unix.h> /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include <stdio.h> + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/usr/utils/Kbuild b/usr/utils/Kbuild new file mode 100644 index 0000000..a389c2a --- /dev/null +++ b/usr/utils/Kbuild @@ -0,0 +1,86 @@ +# +# Kbuild file for klib utils +# + +progs := chroot dd mkdir mkfifo mknod mount pivot_root umount +progs += true false sleep ln mv nuke minips cat ls losetup +progs += insmod uname halt kill readlink cpio sync dmesg + +static-y := $(addprefix static/, $(progs)) +shared-y := $(addprefix shared/, $(progs)) + +# The binary is placed in a subdir, so we need to tell kbuild this +static/chroot-y := chroot.o +shared/chroot-y := chroot.o +static/dd-y := dd.o +shared/dd-y := dd.o +static/dmesg-y := dmesg.o +shared/dmesg-y := dmesg.o +static/mkdir-y := mkdir.o file_mode.o +shared/mkdir-y := mkdir.o file_mode.o +static/mkfifo-y := mkfifo.o file_mode.o +shared/mkfifo-y := mkfifo.o file_mode.o +static/mknod-y := mknod.o file_mode.o +shared/mknod-y := mknod.o file_mode.o +static/mount-y := mount_main.o mount_opts.o +shared/mount-y := mount_main.o mount_opts.o +static/pivot_root-y := pivot_root.o +shared/pivot_root-y := pivot_root.o +static/umount-y := umount.o +shared/umount-y := umount.o +static/true-y := true.o +shared/true-y := true.o +static/false-y := false.o +shared/false-y := false.o +static/sleep-y := sleep.o +shared/sleep-y := sleep.o +static/ln-y := ln.o +shared/ln-y := ln.o +static/ls-y := ls.o +shared/ls-y := ls.o +static/mv-y := mv.o +shared/mv-y := mv.o +static/nuke-y := nuke.o +shared/nuke-y := nuke.o +static/minips-y := minips.o +shared/minips-y := minips.o +static/cat-y := cat.o +shared/cat-y := cat.o +static/insmod-y := insmod.o +shared/insmod-y := insmod.o +static/uname-y := uname.o +shared/uname-y := uname.o +static/halt-y := halt.o +shared/halt-y := halt.o +static/kill-y := kill.o +shared/kill-y := kill.o +static/readlink-y := readlink.o +shared/readlink-y := readlink.o +static/cpio-y := cpio.o +shared/cpio-y := cpio.o +static/sync-y := sync.o +shared/sync-y := sync.o +static/losetup-y := losetup.o +shared/losetup-y := losetup.o + +# Additionally linked targets +always := static/reboot static/poweroff +ifdef KLIBCSHAREDFLAGS +always += shared/reboot shared/poweroff +endif + +$(obj)/static/reboot $(obj)/static/poweroff: $(obj)/static/halt + $(call cmd,ln) +$(obj)/shared/reboot $(obj)/shared/poweroff: $(obj)/shared/halt + $(call cmd,ln) + +# Clean deletes the static and shared dir +clean-dirs := static shared + +# install the shared binaries by preference +ifdef KLIBCSHAREDFLAGS +install-y := $(shared-y) +else +install-y := $(static-y) +endif +install-link-y := reboot=halt poweroff=halt diff --git a/usr/utils/cat.c b/usr/utils/cat.c new file mode 100644 index 0000000..7465148 --- /dev/null +++ b/usr/utils/cat.c @@ -0,0 +1,309 @@ +/* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kevin Fall. + * + * 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. + */ + +#ifndef __COPYRIGHT +#define __COPYRIGHT(arg) +#endif +#ifndef __RCSID +#define __RCSID(arg) +#endif + +#if !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#if 0 +static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95"; +#else +__RCSID("$NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag; +int rval; +const char *filename; + +int main(int, char *[]); +void cook_args(char *argv[]); +void cook_buf(FILE *); +void raw_args(char *argv[]); +void raw_cat(int); + +int main(int argc, char *argv[]) +{ + int ch; + struct flock stdout_lock; + + while ((ch = getopt(argc, argv, "beflnstuv")) != -1) + switch (ch) { + case 'b': + bflag = nflag = 1; /* -b implies -n */ + break; + case 'e': + eflag = vflag = 1; /* -e implies -v */ + break; + case 'f': + fflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + case 't': + tflag = vflag = 1; /* -t implies -v */ + break; + case 'u': + /* unimplemented */ + break; + case 'v': + vflag = 1; + break; + default: + case '?': + (void)fprintf(stderr, + "usage: cat [-beflnstuv] [-] [file ...]\n"); + exit(1); + /* NOTREACHED */ + } + argv += optind; + + if (lflag) { + stdout_lock.l_len = 0; + stdout_lock.l_start = 0; + stdout_lock.l_type = F_WRLCK; + stdout_lock.l_whence = SEEK_SET; + if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) { + perror("fcntl"); + exit(1); + } + } + + if (bflag || eflag || nflag || sflag || tflag || vflag) + cook_args(argv); + else + raw_args(argv); + if (fclose(stdout)) { + perror("fclose"); + exit(1); + } + exit(rval); + /* NOTREACHED */ +} + +void cook_args(char **argv) +{ + FILE *fp; + + fp = stdin; + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fp = stdin; + else if ((fp = fopen(*argv, + fflag ? "rf" : "r")) == NULL) { + perror("fopen"); + rval = 1; + ++argv; + continue; + } + filename = *argv++; + } + cook_buf(fp); + if (fp != stdin) + (void)fclose(fp); + } while (*argv); +} + +void cook_buf(FILE * fp) +{ + int ch, gobble, line, prev; + int stdout_err = 0; + + line = gobble = 0; + for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { + if (prev == '\n') { + if (ch == '\n') { + if (sflag) { + if (!gobble && putchar(ch) == EOF) + break; + gobble = 1; + continue; + } + if (nflag) { + if (!bflag) { + if (fprintf(stdout, + "%6d\t", + ++line) < 0) { + stdout_err++; + break; + } + } else if (eflag) { + if (fprintf(stdout, + "%6s\t", "") < 0) { + stdout_err++; + break; + } + } + } + } else if (nflag) { + if (fprintf(stdout, "%6d\t", ++line) < 0) { + stdout_err++; + break; + } + } + } + gobble = 0; + if (ch == '\n') { + if (eflag) + if (putchar('$') == EOF) + break; + } else if (ch == '\t') { + if (tflag) { + if (putchar('^') == EOF || putchar('I') == EOF) + break; + continue; + } + } else if (vflag) { + if (!isascii(ch)) { + if (putchar('M') == EOF || putchar('-') == EOF) + break; + ch = (ch) & 0x7f; + } + if (iscntrl(ch)) { + if (putchar('^') == EOF || + putchar(ch == '\177' ? '?' : + ch | 0100) == EOF) + break; + continue; + } + } + if (putchar(ch) == EOF) + break; + } + if (stdout_err) { + perror(filename); + rval = 1; + } +} + +void raw_args(char **argv) +{ + int fd; + + fd = fileno(stdin); + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fd = fileno(stdin); + else if (fflag) { + struct stat st; + fd = open(*argv, O_RDONLY | O_NONBLOCK, 0); + if (fd < 0) + goto skip; + + if (fstat(fd, &st) == -1) { + close(fd); + goto skip; + } + if (!S_ISREG(st.st_mode)) { + close(fd); + errno = EINVAL; + goto skipnomsg; + } + } else if ((fd = open(*argv, O_RDONLY, 0)) < 0) { + skip: + perror(*argv); + skipnomsg: + rval = 1; + ++argv; + continue; + } + filename = *argv++; + } + raw_cat(fd); + if (fd != fileno(stdin)) + (void)close(fd); + } while (*argv); +} + +void raw_cat(int rfd) +{ + static char *buf; + static char fb_buf[BUFSIZ]; + static size_t bsize; + + struct stat sbuf; + ssize_t nr, nw, off; + int wfd; + + wfd = fileno(stdout); + if (buf == NULL) { + if (fstat(wfd, &sbuf) == 0) { + bsize = sbuf.st_blksize > BUFSIZ ? + sbuf.st_blksize : BUFSIZ; + buf = malloc(bsize); + } + if (buf == NULL) { + buf = fb_buf; + bsize = BUFSIZ; + } + } + while ((nr = read(rfd, buf, bsize)) > 0) + for (off = 0; nr; nr -= nw, off += nw) + if ((nw = write(wfd, buf + off, (size_t) nr)) < 0) { + perror("write"); + exit(1); + } + if (nr < 0) { + fprintf(stderr, "%s: invalid length\n", filename); + rval = 1; + } +} diff --git a/usr/utils/chroot.c b/usr/utils/chroot.c new file mode 100644 index 0000000..bc1b94f --- /dev/null +++ b/usr/utils/chroot.c @@ -0,0 +1,30 @@ +/* + * by rmk + */ +#include <unistd.h> +#include <stdio.h> + +int main(int argc, char *argv[], char *envp[]) +{ + if (argc < 3) { + fprintf(stderr, "Usage: %s newroot command...\n", argv[0]); + return 1; + } + + if (chroot(argv[1]) == -1) { + perror("chroot"); + return 1; + } + + if (chdir("/") == -1) { + perror("chdir"); + return 1; + } + + if (execvp(argv[2], argv + 2) == -1) { + perror("execvp"); + return 1; + } + + return 0; +} diff --git a/usr/utils/cpio.c b/usr/utils/cpio.c new file mode 100644 index 0000000..9b0b6ae --- /dev/null +++ b/usr/utils/cpio.c @@ -0,0 +1,1079 @@ +/* copyin.c - extract or list a cpio archive + Copyright (C) 1990,1991,1992,2001,2002,2003,2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <malloc.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> +#include <fnmatch.h> + +# ifndef DIRECTORY_SEPARATOR +# define DIRECTORY_SEPARATOR '/' +# endif + +# ifndef ISSLASH +# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR) +# endif + +/* Return 1 if an array of N objects, each of size S, cannot exist due + to size arithmetic overflow. S must be positive and N must be + nonnegative. This is a macro, not an inline function, so that it + works correctly even when SIZE_MAX < N. + + By gnulib convention, SIZE_MAX represents overflow in size + calculations, so the conservative dividend to use here is + SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value. + However, malloc (SIZE_MAX) fails on all known hosts where + sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for + exactly-SIZE_MAX allocations on such hosts; this avoids a test and + branch when S is known to be 1. */ +# define xalloc_oversized(n, s) \ + ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n)) + +#define DISK_IO_BLOCK_SIZE (512) + +char *progname = NULL; + +/* If true, print a . for each file processed. (-V) */ +char dot_flag = false; + +/* Input and output buffers. */ +char *input_buffer, *output_buffer; + +/* The size of the input buffer. */ +long input_buffer_size; + +/* Current locations in `input_buffer' and `output_buffer'. */ +char *in_buff, *out_buff; + +/* Current number of bytes stored at `input_buff' and `output_buff'. */ +long input_size, output_size; + +/* Block size value, initially 512. -B sets to 5120. */ +int io_block_size = 512; + +struct new_cpio_header { + unsigned short c_magic; + union { + struct { + unsigned long c_ino; + unsigned long c_mode; + unsigned long c_uid; + unsigned long c_gid; + unsigned long c_nlink; + unsigned long c_mtime; + unsigned long c_filesize; + long c_dev_maj; + long c_dev_min; + long c_rdev_maj; + long c_rdev_min; + unsigned long c_namesize; + unsigned long c_chksum; + }; + unsigned long c_hdr[13]; + }; + char *c_name; + char *c_tar_linkname; +}; + +/* Total number of bytes read and written for all files. + * Now that many tape drives hold more than 4Gb we need more than 32 + * bits to hold input_bytes and output_bytes. + */ +long long input_bytes, output_bytes; + +/* Allocate N bytes of memory dynamically, with error checking. */ + +static void *xmalloc(size_t n) +{ + void *p; + if (xalloc_oversized(n, 1) || (!(p = malloc(n)) && n != 0)) { + fprintf(stderr, "%s: memory exhausted\n", progname); + exit(1); + } + return p; +/* return xnmalloc_inline (n, 1); */ +} + +/* Clone STRING. */ + +static char *xstrdup(char const *string) +{ + size_t s = strlen(string) + 1; + return memcpy(xmalloc(s), string, s); +/* return xmemdup_inline (string, strlen (string) + 1); */ +} + +/* Copy NUM_BYTES of buffer `in_buff' into IN_BUF. + `in_buff' may be partly full. + When `in_buff' is exhausted, refill it from file descriptor IN_DES. */ + +static void tape_fill_input_buffer(int in_des, int num_bytes) +{ + in_buff = input_buffer; + num_bytes = (num_bytes < io_block_size) ? num_bytes : io_block_size; + input_size = read(in_des, input_buffer, num_bytes); + if (input_size < 0) { + fprintf(stderr, "%s: read error: %s\n", progname, + strerror(errno)); + exit(1); + } + if (input_size == 0) { + fprintf(stderr, "%s: premature end of file\n", progname); + exit(1); + } + input_bytes += input_size; +} + +/* Write `output_size' bytes of `output_buffer' to file + descriptor OUT_DES and reset `output_size' and `out_buff'. + If `swapping_halfwords' or `swapping_bytes' is set, + do the appropriate swapping first. Our callers have + to make sure to only set these flags if `output_size' + is appropriate (a multiple of 4 for `swapping_halfwords', + 2 for `swapping_bytes'). The fact that DISK_IO_BLOCK_SIZE + must always be a multiple of 4 helps us (and our callers) + insure this. */ + +static void disk_empty_output_buffer(int out_des) +{ + int bytes_written; + + bytes_written = write(out_des, output_buffer, output_size); + + if (bytes_written != output_size) { + fprintf(stderr, "%s: write error: %s\n", + progname, strerror(errno)); + exit(1); + } + output_bytes += output_size; + out_buff = output_buffer; + output_size = 0; +} + +/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full. + When `out_buff' fills up, flush it to file descriptor OUT_DES. */ + +static void disk_buffered_write(char *in_buf, int out_des, long num_bytes) +{ + register long bytes_left = num_bytes; /* Bytes needing to be copied. */ + register long space_left; /* Room left in output buffer. */ + + while (bytes_left > 0) { + space_left = DISK_IO_BLOCK_SIZE - output_size; + if (space_left == 0) + disk_empty_output_buffer(out_des); + else { + if (bytes_left < space_left) + space_left = bytes_left; + memmove(out_buff, in_buf, (unsigned)space_left); + out_buff += space_left; + output_size += space_left; + in_buf += space_left; + bytes_left -= space_left; + } + } +} + +/* Copy a file using the input and output buffers, which may start out + partly full. After the copy, the files are not closed nor the last + block flushed to output, and the input buffer may still be partly + full. If `crc_i_flag' is set, add each byte to `crc'. + IN_DES is the file descriptor for input; + OUT_DES is the file descriptor for output; + NUM_BYTES is the number of bytes to copy. */ + +static void copy_files_tape_to_disk(int in_des, int out_des, long num_bytes) +{ + long size; + + while (num_bytes > 0) { + if (input_size == 0) + tape_fill_input_buffer(in_des, io_block_size); + size = (input_size < num_bytes) ? input_size : num_bytes; + disk_buffered_write(in_buff, out_des, size); + num_bytes -= size; + input_size -= size; + in_buff += size; + } +} + +/* if IN_BUF is NULL, Skip the next NUM_BYTES bytes of file descriptor IN_DES. */ +static void tape_buffered_read(char *in_buf, int in_des, long num_bytes) +{ + register long bytes_left = num_bytes; /* Bytes needing to be copied. */ + register long space_left; /* Bytes to copy from input buffer. */ + + while (bytes_left > 0) { + if (input_size == 0) + tape_fill_input_buffer(in_des, io_block_size); + if (bytes_left < input_size) + space_left = bytes_left; + else + space_left = input_size; + if (in_buf != NULL) { + memmove(in_buf, in_buff, (unsigned)space_left); + in_buf += space_left; + } + in_buff += space_left; + input_size -= space_left; + bytes_left -= space_left; + } +} + +/* Skip the next NUM_BYTES bytes of file descriptor IN_DES. */ +#define tape_toss_input(in_des,num_bytes) \ +(tape_buffered_read(NULL,(in_des),(num_bytes))) + +struct deferment { + struct deferment *next; + struct new_cpio_header header; +}; + +static struct deferment *create_deferment(struct new_cpio_header *file_hdr) +{ + struct deferment *d; + d = (struct deferment *)xmalloc(sizeof(struct deferment)); + d->header = *file_hdr; + d->header.c_name = (char *)xmalloc(strlen(file_hdr->c_name) + 1); + strcpy(d->header.c_name, file_hdr->c_name); + return d; +} + +static void free_deferment(struct deferment *d) +{ + free(d->header.c_name); + free(d); +} + +static int link_to_name(char *link_name, char *link_target) +{ + int res = link(link_target, link_name); + return res; +} + +struct inode_val { + unsigned long inode; + unsigned long major_num; + unsigned long minor_num; + char *file_name; +}; + +/* Inode hash table. Allocated by first call to add_inode. */ +static struct inode_val **hash_table = NULL; + +/* Size of current hash table. Initial size is 47. (47 = 2*22 + 3) */ +static int hash_size = 22; + +/* Number of elements in current hash table. */ +static int hash_num; + +/* Do the hash insert. Used in normal inserts and resizing the hash + table. It is guaranteed that there is room to insert the item. + NEW_VALUE is the pointer to the previously allocated inode, file + name association record. */ + +static void hash_insert(struct inode_val *new_value) +{ + int start; /* Home position for the value. */ + int temp; /* Used for rehashing. */ + + /* Hash function is node number modulo the table size. */ + start = new_value->inode % hash_size; + + /* Do the initial look into the table. */ + if (hash_table[start] == NULL) { + hash_table[start] = new_value; + return; + } + + /* If we get to here, the home position is full with a different inode + record. Do a linear search for the first NULL pointer and insert + the new item there. */ + temp = (start + 1) % hash_size; + while (hash_table[temp] != NULL) + temp = (temp + 1) % hash_size; + + /* Insert at the NULL. */ + hash_table[temp] = new_value; +} + +/* Associate FILE_NAME with the inode NODE_NUM. (Insert into hash table.) */ + +static void +add_inode(unsigned long node_num, char *file_name, unsigned long major_num, + unsigned long minor_num) +{ + struct inode_val *temp; + + /* Create new inode record. */ + temp = (struct inode_val *)xmalloc(sizeof(struct inode_val)); + temp->inode = node_num; + temp->major_num = major_num; + temp->minor_num = minor_num; + temp->file_name = xstrdup(file_name); + + /* Do we have to increase the size of (or initially allocate) + the hash table? */ + if (hash_num == hash_size || hash_table == NULL) { + struct inode_val **old_table; /* Pointer to old table. */ + int i; /* Index for re-insert loop. */ + + /* Save old table. */ + old_table = hash_table; + if (old_table == NULL) + hash_num = 0; + + /* Calculate new size of table and allocate it. + Sequence of table sizes is 47, 97, 197, 397, 797, 1597, 3197, 6397 ... + where 3197 and most of the sizes after 6397 are not prime. The other + numbers listed are prime. */ + hash_size = 2 * hash_size + 3; + hash_table = (struct inode_val **) + xmalloc(hash_size * sizeof(struct inode_val *)); + memset(hash_table, 0, hash_size * sizeof(struct inode_val *)); + + /* Insert the values from the old table into the new table. */ + for (i = 0; i < hash_num; i++) + hash_insert(old_table[i]); + + free(old_table); + } + + /* Insert the new record and increment the count of elements in the + hash table. */ + hash_insert(temp); + hash_num++; +} + +static char *find_inode_file(unsigned long node_num, unsigned long major_num, + unsigned long minor_num) +{ + int start; /* Initial hash location. */ + int temp; /* Rehash search variable. */ + + if (hash_table != NULL) { + /* Hash function is node number modulo the table size. */ + start = node_num % hash_size; + + /* Initial look into the table. */ + if (hash_table[start] == NULL) + return NULL; + if (hash_table[start]->inode == node_num + && hash_table[start]->major_num == major_num + && hash_table[start]->minor_num == minor_num) + return hash_table[start]->file_name; + + /* The home position is full with a different inode record. + Do a linear search terminated by a NULL pointer. */ + for (temp = (start + 1) % hash_size; + hash_table[temp] != NULL && temp != start; + temp = (temp + 1) % hash_size) { + if (hash_table[temp]->inode == node_num + && hash_table[start]->major_num == major_num + && hash_table[start]->minor_num == minor_num) + return hash_table[temp]->file_name; + } + } + return NULL; +} + +/* Try and create a hard link from FILE_NAME to another file + with the given major/minor device number and inode. If no other + file with the same major/minor/inode numbers is known, add this file + to the list of known files and associated major/minor/inode numbers + and return -1. If another file with the same major/minor/inode + numbers is found, try and create another link to it using + link_to_name, and return 0 for success and -1 for failure. */ + +static int +link_to_maj_min_ino(char *file_name, int st_dev_maj, int st_dev_min, int st_ino) +{ + int link_res; + char *link_name; + link_res = -1; + /* Is the file a link to a previously copied file? */ + link_name = find_inode_file(st_ino, st_dev_maj, st_dev_min); + if (link_name == NULL) + add_inode(st_ino, file_name, st_dev_maj, st_dev_min); + else + link_res = link_to_name(file_name, link_name); + return link_res; +} + +static void copyin_regular_file(struct new_cpio_header *file_hdr, + int in_file_des); + +static void warn_junk_bytes(long bytes_skipped) +{ + fprintf(stderr, "%s: warning: skipped %ld byte(s) of junk\n", + progname, bytes_skipped); +} + +/* Skip the padding on IN_FILE_DES after a header or file, + up to the next header. + The number of bytes skipped is based on OFFSET -- the current offset + from the last start of a header (or file) -- and the current + header type. */ + +static void tape_skip_padding(int in_file_des, int offset) +{ + int pad; + pad = (4 - (offset % 4)) % 4; + + if (pad != 0) + tape_toss_input(in_file_des, pad); +} + +static int +try_existing_file(struct new_cpio_header *file_hdr, int in_file_des, + int *existing_dir) +{ + struct stat file_stat; + + *existing_dir = false; + if (lstat(file_hdr->c_name, &file_stat) == 0) { + if (S_ISDIR(file_stat.st_mode) + && ((file_hdr->c_mode & S_IFMT) == S_IFDIR)) { + /* If there is already a directory there that + we are trying to create, don't complain about + it. */ + *existing_dir = true; + return 0; + } else if (S_ISDIR(file_stat.st_mode) + ? rmdir(file_hdr->c_name) + : unlink(file_hdr->c_name)) { + fprintf(stderr, "%s: cannot remove current %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + tape_toss_input(in_file_des, file_hdr->c_filesize); + tape_skip_padding(in_file_des, file_hdr->c_filesize); + return -1; /* Go to the next file. */ + } + } + return 0; +} + +/* The newc and crc formats store multiply linked copies of the same file + in the archive only once. The actual data is attached to the last link + in the archive, and the other links all have a filesize of 0. When a + file in the archive has multiple links and a filesize of 0, its data is + probably "attatched" to another file in the archive, so we can't create + it right away. We have to "defer" creating it until we have created + the file that has the data "attatched" to it. We keep a list of the + "defered" links on deferments. */ + +struct deferment *deferments = NULL; + +/* Add a file header to the deferments list. For now they all just + go on one list, although we could optimize this if necessary. */ + +static void defer_copyin(struct new_cpio_header *file_hdr) +{ + struct deferment *d; + d = create_deferment(file_hdr); + d->next = deferments; + deferments = d; + return; +} + +/* We just created a file that (probably) has some other links to it + which have been defered. Go through all of the links on the deferments + list and create any which are links to this file. */ + +static void create_defered_links(struct new_cpio_header *file_hdr) +{ + struct deferment *d; + struct deferment *d_prev; + int ino; + int maj; + int min; + int link_res; + ino = file_hdr->c_ino; + maj = file_hdr->c_dev_maj; + min = file_hdr->c_dev_min; + d = deferments; + d_prev = NULL; + while (d != NULL) { + if ((d->header.c_ino == ino) && (d->header.c_dev_maj == maj) + && (d->header.c_dev_min == min)) { + struct deferment *d_free; + link_res = + link_to_name(d->header.c_name, file_hdr->c_name); + if (link_res < 0) { + fprintf(stderr, + "%s: cannot link %s to %s: %s\n", + progname, d->header.c_name, + file_hdr->c_name, strerror(errno)); + } + if (d_prev != NULL) + d_prev->next = d->next; + else + deferments = d->next; + d_free = d; + d = d->next; + free_deferment(d_free); + } else { + d_prev = d; + d = d->next; + } + } +} + +/* If we had a multiply linked file that really was empty then we would + have defered all of its links, since we never found any with data + "attached", and they will still be on the deferment list even when + we are done reading the whole archive. Write out all of these + empty links that are still on the deferments list. */ + +static void create_final_defers(void) +{ + struct deferment *d; + int link_res; + int out_file_des; + struct utimbuf times; /* For setting file times. */ + /* Initialize this in case it has members we don't know to set. */ + memset(×, 0, sizeof(struct utimbuf)); + + for (d = deferments; d != NULL; d = d->next) { + /* Debian hack: A line, which could cause an endless loop, was + removed (97/1/2). It was reported by Ronald F. Guilmette to + the upstream maintainers. -BEM */ + /* Debian hack: This was reported by Horst Knobloch. This bug has + been reported to "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM + */ + link_res = link_to_maj_min_ino(d->header.c_name, + d->header.c_dev_maj, + d->header.c_dev_min, + d->header.c_ino); + if (link_res == 0) { + continue; + } + out_file_des = open(d->header.c_name, O_CREAT | O_WRONLY, 0600); + if (out_file_des < 0) { + fprintf(stderr, "%s: open %s: %s\n", + progname, d->header.c_name, strerror(errno)); + continue; + } + + /* File is now copied; set attributes. */ + if ((fchown(out_file_des, d->header.c_uid, d->header.c_gid) < 0) + && errno != EPERM) + fprintf(stderr, "%s: fchown %s: %s\n", + progname, d->header.c_name, strerror(errno)); + /* chown may have turned off some permissions we wanted. */ + if (fchmod(out_file_des, (int)d->header.c_mode) < 0) + fprintf(stderr, "%s: fchmod %s: %s\n", + progname, d->header.c_name, strerror(errno)); + + if (close(out_file_des) < 0) + fprintf(stderr, "%s: close %s: %s\n", + progname, d->header.c_name, strerror(errno)); + + } +} + +static void +copyin_regular_file(struct new_cpio_header *file_hdr, int in_file_des) +{ + int out_file_des; /* Output file descriptor. */ + + /* Can the current file be linked to a previously copied file? */ + if (file_hdr->c_nlink > 1) { + int link_res; + if (file_hdr->c_filesize == 0) { + /* The newc and crc formats store multiply linked copies + of the same file in the archive only once. The + actual data is attached to the last link in the + archive, and the other links all have a filesize + of 0. Since this file has multiple links and a + filesize of 0, its data is probably attatched to + another file in the archive. Save the link, and + process it later when we get the actual data. We + can't just create it with length 0 and add the + data later, in case the file is readonly. We still + lose if its parent directory is readonly (and we aren't + running as root), but there's nothing we can do about + that. */ + defer_copyin(file_hdr); + tape_toss_input(in_file_des, file_hdr->c_filesize); + tape_skip_padding(in_file_des, file_hdr->c_filesize); + return; + } + /* If the file has data (filesize != 0), then presumably + any other links have already been defer_copyin'ed(), + but GNU cpio version 2.0-2.2 didn't do that, so we + still have to check for links here (and also in case + the archive was created and later appeneded to). */ + /* Debian hack: (97/1/2) This was reported by Ronald + F. Guilmette to the upstream maintainers. -BEM */ + link_res = link_to_maj_min_ino(file_hdr->c_name, + file_hdr->c_dev_maj, + file_hdr->c_dev_min, + file_hdr->c_ino); + if (link_res == 0) { + tape_toss_input(in_file_des, file_hdr->c_filesize); + tape_skip_padding(in_file_des, file_hdr->c_filesize); + return; + } + } + + /* If not linked, copy the contents of the file. */ + out_file_des = open(file_hdr->c_name, O_CREAT | O_WRONLY, 0600); + + if (out_file_des < 0) { + fprintf(stderr, "%s: open %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + tape_toss_input(in_file_des, file_hdr->c_filesize); + tape_skip_padding(in_file_des, file_hdr->c_filesize); + return; + } + + copy_files_tape_to_disk(in_file_des, out_file_des, + file_hdr->c_filesize); + disk_empty_output_buffer(out_file_des); + + if (close(out_file_des) < 0) + fprintf(stderr, "%s: close %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + + /* File is now copied; set attributes. */ + if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0) + && errno != EPERM) + fprintf(stderr, "%s: chown %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + + /* chown may have turned off some permissions we wanted. */ + if (chmod(file_hdr->c_name, (int)file_hdr->c_mode) < 0) + fprintf(stderr, "%s: chmod %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + + tape_skip_padding(in_file_des, file_hdr->c_filesize); + if (file_hdr->c_nlink > 1) { + /* (see comment above for how the newc and crc formats + store multiple links). Now that we have the data + for this file, create any other links to it which + we defered. */ + create_defered_links(file_hdr); + } +} + +/* In general, we can't use the builtin `basename' function if available, + since it has different meanings in different environments. + In some environments the builtin `basename' modifies its argument. + + Return the address of the last file name component of NAME. If + NAME has no file name components because it is all slashes, return + NAME if it is empty, the address of its last slash otherwise. */ + +static char *base_name(char const *name) +{ + char const *base = name; + char const *p; + + for (p = base; *p; p++) { + if (ISSLASH(*p)) { + /* Treat multiple adjacent slashes like a single slash. */ + do + p++; + while (ISSLASH(*p)); + + /* If the file name ends in slash, use the trailing slash as + the basename if no non-slashes have been found. */ + if (!*p) { + if (ISSLASH(*base)) + base = p - 1; + break; + } + + /* *P is a non-slash preceded by a slash. */ + base = p; + } + } + + return (char *)base; +} + +/* Return the length of of the basename NAME. Typically NAME is the + value returned by base_name. Act like strlen (NAME), except omit + redundant trailing slashes. */ + +static size_t base_len(char const *name) +{ + size_t len; + + for (len = strlen(name); 1 < len && ISSLASH(name[len - 1]); len--) + continue; + + return len; +} + +/* Remove trailing slashes from PATH. + Return true if a trailing slash was removed. + This is useful when using filename completion from a shell that + adds a "/" after directory names (such as tcsh and bash), because + the Unix rename and rmdir system calls return an "Invalid argument" error + when given a path that ends in "/" (except for the root directory). */ + +static bool strip_trailing_slashes(char *path) +{ + char *base = base_name(path); + char *base_lim = base + base_len(base); + bool had_slash = (*base_lim != '\0'); + *base_lim = '\0'; + return had_slash; +} + +static void copyin_directory(struct new_cpio_header *file_hdr, int existing_dir) +{ + int res; /* Result of various function calls. */ + + /* Strip any trailing `/'s off the filename; tar puts + them on. We might as well do it here in case anybody + else does too, since they cause strange things to happen. */ + strip_trailing_slashes(file_hdr->c_name); + + /* Ignore the current directory. It must already exist, + and we don't want to change its permission, ownership + or time. */ + if (file_hdr->c_name[0] == '.' && file_hdr->c_name[1] == '\0') { + return; + } + + if (!existing_dir) + { + res = mkdir(file_hdr->c_name, file_hdr->c_mode); + } else + res = 0; + if (res < 0) { + /* In some odd cases where the file_hdr->c_name includes `.', + the directory may have actually been created by + create_all_directories(), so the mkdir will fail + because the directory exists. If that's the case, + don't complain about it. */ + struct stat file_stat; + if ((errno != EEXIST) || + (lstat(file_hdr->c_name, &file_stat) != 0) || + !(S_ISDIR(file_stat.st_mode))) { + fprintf(stderr, "%s: lstat %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + return; + } + } + if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0) + && errno != EPERM) + fprintf(stderr, "%s: chown %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + /* chown may have turned off some permissions we wanted. */ + if (chmod(file_hdr->c_name, (int)file_hdr->c_mode) < 0) + fprintf(stderr, "%s: chmod %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); +} + +static void copyin_device(struct new_cpio_header *file_hdr) +{ + int res; /* Result of various function calls. */ + + if (file_hdr->c_nlink > 1) { + int link_res; + /* Debian hack: This was reported by Horst + Knobloch. This bug has been reported to + "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */ + link_res = link_to_maj_min_ino(file_hdr->c_name, + file_hdr->c_dev_maj, + file_hdr->c_dev_min, + file_hdr->c_ino); + if (link_res == 0) { + return; + } + } + + res = mknod(file_hdr->c_name, file_hdr->c_mode, + makedev(file_hdr->c_rdev_maj, file_hdr->c_rdev_min)); + if (res < 0) { + fprintf(stderr, "%s: mknod %s: %s\n", progname, + file_hdr->c_name, strerror(errno)); + return; + } + if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0) + && errno != EPERM) + fprintf(stderr, "%s: chown %s: %s\n", progname, + file_hdr->c_name, strerror(errno)); + /* chown may have turned off some permissions we wanted. */ + if (chmod(file_hdr->c_name, file_hdr->c_mode) < 0) + fprintf(stderr, "%s: chmod %s: %s\n", progname, + file_hdr->c_name, strerror(errno)); +} + +static void copyin_link(struct new_cpio_header *file_hdr, int in_file_des) +{ + char *link_name = NULL; /* Name of hard and symbolic links. */ + int res; /* Result of various function calls. */ + + link_name = (char *)xmalloc(file_hdr->c_filesize + 1); + link_name[file_hdr->c_filesize] = '\0'; + tape_buffered_read(link_name, in_file_des, file_hdr->c_filesize); + tape_skip_padding(in_file_des, file_hdr->c_filesize); + + res = symlink(link_name, file_hdr->c_name); + if (res < 0) { + fprintf(stderr, "%s: symlink %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + free(link_name); + return; + } + if ((lchown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0) + && errno != EPERM) { + fprintf(stderr, "%s: lchown %s: %s\n", + progname, file_hdr->c_name, strerror(errno)); + } + free(link_name); +} + +static void copyin_file(struct new_cpio_header *file_hdr, int in_file_des) +{ + int existing_dir; + + if (try_existing_file(file_hdr, in_file_des, &existing_dir) < 0) + return; + + /* Do the real copy or link. */ + switch (file_hdr->c_mode & S_IFMT) { + case S_IFREG: + copyin_regular_file(file_hdr, in_file_des); + break; + + case S_IFDIR: + copyin_directory(file_hdr, existing_dir); + break; + + case S_IFCHR: + case S_IFBLK: + case S_IFSOCK: + case S_IFIFO: + copyin_device(file_hdr); + break; + + case S_IFLNK: + copyin_link(file_hdr, in_file_des); + break; + + default: + fprintf(stderr, "%s: %s: unknown file type\n", + progname, file_hdr->c_name); + tape_toss_input(in_file_des, file_hdr->c_filesize); + tape_skip_padding(in_file_des, file_hdr->c_filesize); + } +} + +/* Fill in FILE_HDR by reading a new-format ASCII format cpio header from + file descriptor IN_DES, except for the magic number, which is + already filled in. */ + +static void read_in_new_ascii(struct new_cpio_header *file_hdr, int in_des) +{ + char ascii_header[13*8], *ah, hexbuf[9]; + int i; + + tape_buffered_read(ascii_header, in_des, 13*8); + ah = ascii_header; + hexbuf[8] = '\0'; + for (i = 0; i < 13; i++) { + memcpy(hexbuf, ah, 8); + file_hdr->c_hdr[i] = strtoul(hexbuf, NULL, 16); + ah += 8; + } + + /* Sizes > LONG_MAX can currently result in integer overflow + in various places. Fail if name is too large. */ + if (file_hdr->c_namesize > LONG_MAX) { + fprintf(stderr, "%s: name size out of range\n", + progname); + exit(1); + } + + /* Read file name from input. */ + free(file_hdr->c_name); + file_hdr->c_name = (char *)xmalloc(file_hdr->c_namesize); + tape_buffered_read(file_hdr->c_name, in_des, + (long)file_hdr->c_namesize); + + /* In SVR4 ASCII format, the amount of space allocated for the header + is rounded up to the next long-word, so we might need to drop + 1-3 bytes. */ + tape_skip_padding(in_des, file_hdr->c_namesize + 110); + + /* Fail if file is too large. We could check this earlier + but it's helpful to report the name. */ + if (file_hdr->c_filesize > LONG_MAX) { + fprintf(stderr, "%s: %s: file size out of range\n", + progname, file_hdr->c_name); + exit(1); + } +} + +/* Return 16-bit integer I with the bytes swapped. */ +#define swab_short(i) ((((i) << 8) & 0xff00) | (((i) >> 8) & 0x00ff)) + +/* Read the header, including the name of the file, from file + descriptor IN_DES into FILE_HDR. */ + +static void read_in_header(struct new_cpio_header *file_hdr, int in_des) +{ + long bytes_skipped = 0; /* Bytes of junk found before magic number. */ + + /* Search for a valid magic number. */ + + file_hdr->c_tar_linkname = NULL; + + tape_buffered_read((char *)file_hdr, in_des, 6L); + while (1) { + if (!strncmp((char *)file_hdr, "070702", 6) + || !strncmp((char *)file_hdr, "070701", 6)) + { + if (bytes_skipped > 0) + warn_junk_bytes(bytes_skipped); + + read_in_new_ascii(file_hdr, in_des); + break; + } + bytes_skipped++; + memmove((char *)file_hdr, (char *)file_hdr + 1, 5); + tape_buffered_read((char *)file_hdr + 5, in_des, 1L); + } +} + +/* Read the collection from standard input and create files + in the file system. */ + +static void process_copy_in(void) +{ + char done = false; /* True if trailer reached. */ + struct new_cpio_header file_hdr; /* Output header information. */ + int in_file_des; /* Input file descriptor. */ + + /* Initialize the copy in. */ + file_hdr.c_name = NULL; + + /* only from stdin */ + in_file_des = 0; + + /* While there is more input in the collection, process the input. */ + while (!done) { + /* Start processing the next file by reading the header. */ + read_in_header(&file_hdr, in_file_des); + + /* Is this the header for the TRAILER file? */ + if (strcmp("TRAILER!!!", file_hdr.c_name) == 0) { + done = true; + break; + } + + /* Copy the input file into the directory structure. */ + + copyin_file(&file_hdr, in_file_des); + + if (dot_flag) + fputc('.', stderr); + } + + if (dot_flag) + fputc('\n', stderr); + + create_final_defers(); + +} + +/* Initialize the input and output buffers to their proper size and + initialize all variables associated with the input and output + buffers. */ + +static void initialize_buffers(void) +{ + int in_buf_size, out_buf_size; + + /* Make sure the input buffer can always hold 2 blocks and that it + is big enough to hold 1 tar record (512 bytes) even if it + is not aligned on a block boundary. The extra buffer space + is needed by process_copyin and peek_in_buf to automatically + figure out what kind of archive it is reading. */ + if (io_block_size >= 512) + in_buf_size = 2 * io_block_size; + else + in_buf_size = 1024; + out_buf_size = DISK_IO_BLOCK_SIZE; + + input_buffer = (char *)xmalloc(in_buf_size); + in_buff = input_buffer; + input_buffer_size = in_buf_size; + input_size = 0; + input_bytes = 0; + + output_buffer = (char *)xmalloc(out_buf_size); + out_buff = output_buffer; + output_size = 0; + output_bytes = 0; + +} + +int main(int argc, char *argv[]) +{ + int c; + int extract_flag = false; + + progname = argv[0]; + + do { + c = getopt(argc, argv, "iV"); + if (c == EOF) + break; + switch (c) { + case 'V': + dot_flag = true; + break; + + case 'i': + extract_flag = true; + break; + case '?': + fprintf(stderr, + "%s: not implemented or invalid option -%c\n", + progname, optopt); + exit(1); + + } + } while (1); + + if (extract_flag) { + initialize_buffers(); + + process_copy_in(); + } else { + fprintf(stderr, "Usage: %s [-V] -i [< archive]\n", progname); + exit(1); + } + + return 0; +} diff --git a/usr/utils/dd.c b/usr/utils/dd.c new file mode 100644 index 0000000..706b8c3 --- /dev/null +++ b/usr/utils/dd.c @@ -0,0 +1,540 @@ +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +static char *progname; + +struct option { + const char *opt; + char *str; + char *arg; +}; + +struct conv { + const char str[8]; + unsigned int set; + unsigned int exclude; +}; + +#define CONV_BLOCK (1<<0) +#define CONV_UNBLOCK (1<<1) + +#define CONV_LCASE (1<<2) +#define CONV_UCASE (1<<3) + +#define CONV_SWAB (1<<4) +#define CONV_NOERROR (1<<5) +#define CONV_NOTRUNC (1<<6) +#define CONV_SYNC (1<<7) + +static struct option options[] = { + {"bs", NULL, NULL}, +#define OPT_BS (&options[0]) + {"cbs", NULL, NULL}, +#define OPT_CBS (&options[1]) + {"conv", NULL, NULL}, +#define OPT_CONV (&options[2]) + {"count", NULL, NULL}, +#define OPT_COUNT (&options[3]) + {"ibs", NULL, NULL}, +#define OPT_IBS (&options[4]) + {"if", NULL, NULL}, +#define OPT_IF (&options[5]) + {"obs", NULL, NULL}, +#define OPT_OBS (&options[6]) + {"of", NULL, NULL}, +#define OPT_OF (&options[7]) + {"seek", NULL, NULL}, +#define OPT_SEEK (&options[8]) + {"skip", NULL, NULL} +#define OPT_SKIP (&options[9]) +}; + +static const struct conv conv_opts[] = { + {"block", CONV_BLOCK, CONV_UNBLOCK}, + {"unblock", CONV_UNBLOCK, CONV_BLOCK}, + {"lcase", CONV_LCASE, CONV_UCASE}, + {"ucase", CONV_UCASE, CONV_LCASE}, + {"swab", CONV_SWAB, 0}, + {"noerror", CONV_NOERROR, 0}, + {"notrunc", CONV_NOTRUNC, 0}, + {"sync", CONV_SYNC, 0}, +}; + +static size_t cbs; +static unsigned int conv; +static unsigned int count; +static size_t ibs = 512; +static size_t obs = 512; +static unsigned int seek; +static unsigned int skip; +static char *in_buf; +static char *out_buf; + +static size_t parse_bs(struct option *opt) +{ + unsigned long val, realval = 1; + char *str = opt->str; + int err = 0; + + do { + char *s = str; + val = strtoul(str, &str, 10); + if (s == str || (val == ULONG_MAX && errno == ERANGE)) { + err = 1; + break; + } + + /* + * This option may be followed by + * 'b', 'k' or 'x' + */ + if (*str == 'b') { + val *= 512; + str++; + } else if (*str == 'k') { + val *= 1024; + str++; + } + realval *= val; + if (*str != 'x') + break; + str++; + } while (1); + + if (*str != '\0') + err = 1; + + if (err) { + fprintf(stderr, "%s: bad operand `%s'\n", progname, opt->arg); + exit(1); + } + + return (size_t) realval; +} + +static unsigned int parse_num(struct option *opt) +{ + unsigned long val; + char *str = opt->str; + + val = strtoul(str, &str, 10); + if (str == opt->str || (val == ULONG_MAX && errno == ERANGE) || + val > UINT_MAX) { + fprintf(stderr, "%s: bad operand `%s'\n", progname, opt->arg); + exit(1); + } + + return (unsigned int)val; +} + +static int parse_options(int argc, char *argv[]) +{ + unsigned int i; + char *p, *s; + int arg; + + /* + * We cheat here; we don't parse the operand values + * themselves here. We merely split the operands + * up. This means that bs=foo bs=1 won't produce + * an error. + */ + for (arg = 1; arg < argc; arg++) { + unsigned int len; + + s = strchr(argv[arg], '='); + if (!s) + s = argv[arg]; /* don't recognise this arg */ + + len = s - argv[arg]; + for (i = 0; i < ARRAY_SIZE(options); i++) { + if (strncmp(options[i].opt, argv[arg], len) != 0) + continue; + + options[i].str = s + 1; + options[i].arg = argv[arg]; + break; + } + + if (i == ARRAY_SIZE(options)) { + fprintf(stderr, "%s: bad operand `%s'\n", + progname, argv[arg]); + return 1; + } + } + + /* + * Translate numeric operands. + */ + if (OPT_IBS->str) + ibs = parse_bs(OPT_IBS); + if (OPT_OBS->str) + obs = parse_bs(OPT_OBS); + if (OPT_CBS->str) + cbs = parse_bs(OPT_CBS); + if (OPT_COUNT->str) + count = parse_num(OPT_COUNT); + if (OPT_SEEK->str) + seek = parse_num(OPT_SEEK); + if (OPT_SKIP->str) + skip = parse_num(OPT_SKIP); + + /* + * If bs= is specified, it overrides ibs= and obs= + */ + if (OPT_BS->str) + ibs = obs = parse_bs(OPT_BS); + + /* + * And finally conv= + */ + if (OPT_CONV->str) { + p = OPT_CONV->str; + + while ((s = strsep(&p, ",")) != NULL) { + for (i = 0; i < ARRAY_SIZE(conv_opts); i++) { + if (strcmp(s, conv_opts[i].str) != 0) + continue; + conv &= ~conv_opts[i].exclude; + conv |= conv_opts[i].set; + break; + } + + if (i == ARRAY_SIZE(conv_opts)) { + fprintf(stderr, "%s: bad conversion `%s'\n", + progname, s); + return 1; + } + } + } + + if (conv & (CONV_BLOCK | CONV_UNBLOCK) && cbs == 0) { + fprintf(stderr, "%s: block/unblock conversion with zero cbs\n", + progname); + return 1; + } + + return 0; +} + +static int safe_read(int fd, void *buf, size_t size) +{ + int ret, count = 0; + char *p = buf; + + while (size) { + ret = read(fd, p, size); + + /* + * If we got EINTR, go again. + */ + if (ret == -1 && errno == EINTR) + continue; + + /* + * If we encountered an error condition + * or read 0 bytes (EOF) return what we + * have. + */ + if (ret == -1 || ret == 0) + return count ? count : ret; + + /* + * We read some bytes. + */ + count += ret; + size -= ret; + p += ret; + } + + return count; +} + +static int skip_blocks(int fd, void *buf, unsigned int blks, size_t size) +{ + unsigned int blk; + int ret = 0; + + /* + * Try to seek. + */ + for (blk = 0; blk < blks; blk++) { + ret = lseek(fd, size, SEEK_CUR); + if (ret == -1) + break; + } + + /* + * If we failed to seek, read instead. + * FIXME: we don't handle short reads here, or + * EINTR correctly. + */ + if (blk == 0 && ret == -1 && errno == ESPIPE) { + for (blk = 0; blk < blks; blk++) { + ret = safe_read(fd, buf, size); + if (ret != (int)size) + break; + } + } + + if (ret == -1) { + perror("seek/skip"); + return 1; + } + return 0; +} + +struct stats { + unsigned int in_full; + unsigned int in_partial; + unsigned int out_full; + unsigned int out_partial; + unsigned int truncated; +}; + +static int do_dd(int rd, int wr, struct stats *stats) +{ + unsigned int i; + int ret; + int fill_val = 0; + size_t out_size = 0; + size_t in_size; + char *buf; + + if (conv & (CONV_BLOCK | CONV_UNBLOCK)) + fill_val = ' '; + + while (!OPT_COUNT->str || count-- != 0) { + buf = in_buf; + + /* + * 1. read ibs-sized buffer + */ + in_size = ret = read(rd, in_buf, ibs); + if (ret == -1 || (ret == 0 && (conv & CONV_NOERROR) == 0)) + break; + + if (in_size == ibs) { + stats->in_full++; + } else { + stats->in_partial++; + + /* + * 2. zero (or append spaces) + */ + if (conv & CONV_SYNC) { + memset(in_buf + in_size, fill_val, + ibs - in_size); + in_size = ibs; + } + } + + /* + * 4. swab conversion. With an odd number of bytes, + * last byte does not get swapped. + */ + if (conv & CONV_SWAB) { + char c; + + for (i = 1; i < in_size; i += 2) { + c = in_buf[i - 1]; + in_buf[i - 1] = in_buf[i]; + in_buf[i] = c; + } + } + + /* + * 5. remaining conversions. + */ + if (conv & CONV_LCASE) + for (i = 0; i < in_size; i++) + in_buf[i] = tolower(in_buf[i]); + + if (conv & CONV_UCASE) + for (i = 0; i < in_size; i++) + in_buf[i] = toupper(in_buf[i]); + + /* block/unblock ? */ + + /* + * 6. Aggregate into obs sized buffers. + * If the in_size is obs-sized and we have no + * data waiting, just write "buf" to the output. + */ + if (out_size == 0 && in_size == obs) { + write(wr, buf, obs); + stats->out_full++; + } else { + /* + * We had data waiting, or we didn't have an + * obs-sized input block. We need to append + * the input data to the output buffer. + */ + unsigned int space; + char *in_ptr = in_buf; + + do { + space = obs - out_size; + if (space > in_size) + space = in_size; + + memcpy(out_buf + out_size, in_ptr, space); + out_size += space; + in_size -= space; + in_ptr += space; + + if (out_size == obs) { + write(wr, out_buf, obs); + stats->out_full++; + out_size = 0; + } + } while (out_size == 0 && in_size); + + if (in_size) { + memcpy(out_buf, in_ptr, in_size); + out_size = in_size; + } + } + } + + if (out_size) { + write(wr, out_buf, out_size); + stats->out_partial++; + } + + return 0; +} + +static sigjmp_buf jmp; + +static void sigint_handler(int sig) +{ + siglongjmp(jmp, -sig); +} + +static int dd(int rd_fd, int wr_fd, struct stats *stats) +{ + int ret; + + ret = sigsetjmp(jmp, 1); + if (ret == 0) { + sysv_signal(SIGINT, sigint_handler); + ret = do_dd(rd_fd, wr_fd, stats); + } + + sysv_signal(SIGINT, SIG_DFL); + return ret; +} + +int main(int argc, char *argv[]) +{ + struct stats stats; + int ret; + int rd_fd = 0, wr_fd = 1; + + progname = argv[0]; + + ret = parse_options(argc, argv); + if (ret) + return ret; + + if (conv & (CONV_BLOCK | CONV_UNBLOCK)) { + fprintf(stderr, "%s: block/unblock not implemented\n", + progname); + return 1; + } + + in_buf = malloc(ibs); + if (!in_buf) { + perror("malloc ibs"); + return 1; + } + + out_buf = malloc(obs); + if (!out_buf) { + perror("malloc obs"); + return 1; + } + + /* + * Open the input file, if specified. + */ + if (OPT_IF->str) { + rd_fd = open(OPT_IF->str, O_RDONLY); + if (rd_fd == -1) { + perror("open input file"); + return 1; + } + } + + /* + * Open the output file, if specified. + */ + if (OPT_OF->str) { + int flags = O_WRONLY|O_CREAT; + flags |= (conv & CONV_NOTRUNC) ? 0 : O_TRUNC; + wr_fd = open(OPT_OF->str, flags, 0666); + if (wr_fd == -1) { + perror("open output file"); + close(rd_fd); + return 1; + } + } + + /* + * Skip obs-sized blocks of output file. + */ + if (OPT_SEEK->str && skip_blocks(wr_fd, out_buf, seek, obs)) { + close(rd_fd); + close(wr_fd); + return 1; + } + + /* + * Skip ibs-sized blocks of input file. + */ + if (OPT_SKIP->str && skip_blocks(rd_fd, in_buf, skip, ibs)) { + close(rd_fd); + close(wr_fd); + return 1; + } + + memset(&stats, 0, sizeof(stats)); + + /* + * Do the real work + */ + ret = dd(rd_fd, wr_fd, &stats); + + if (close(rd_fd) == -1) + perror(OPT_IF->str ? OPT_IF->str : "stdin"); + if (close(wr_fd) == -1) + perror(OPT_OF->str ? OPT_OF->str : "stdout"); + + fprintf(stderr, "%u+%u records in\n", stats.in_full, stats.in_partial); + fprintf(stderr, "%u+%u records out\n", + stats.out_full, stats.out_partial); + if (stats.truncated) + fprintf(stderr, "%u truncated record%s\n", + stats.truncated, stats.truncated == 1 ? "" : "s"); + + /* + * ret will be -SIGINT if we got a SIGINT. Raise + * the signal again to cause us to terminate with + * SIGINT status. + */ + if (ret == -SIGINT) + raise(SIGINT); + + return ret; +} diff --git a/usr/utils/dmesg.c b/usr/utils/dmesg.c new file mode 100644 index 0000000..1960713 --- /dev/null +++ b/usr/utils/dmesg.c @@ -0,0 +1,79 @@ +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <sys/klog.h> + +static void usage(char *name) +{ + fprintf(stderr, "usage: %s [-c]\n", name); +} + +int main(int argc, char *argv[]) +{ + char *buf = NULL; + const char *p; + int c; + int bufsz = 0; + int cmd = 3; /* Read all messages remaining in the ring buffer */ + int len = 0; + int opt; + int newline; + + while ((opt = getopt(argc, argv, "c")) != -1) { + switch (opt) { + /* Read and clear all messages remaining in the ring buffer */ + case 'c': + cmd = 4; + break; + case '?': + default: + usage(argv[0]); + exit(1); + } + } + + if (!bufsz) { + len = klogctl(10, NULL, 0); /* Get size of log buffer */ + if (len > 0) + bufsz = len; + } + + if (bufsz) { + int sz = bufsz + 8; + + buf = (char *)malloc(sz); + len = klogctl(cmd, buf, sz); + } + + if (len < 0) { + perror("klogctl"); + exit(1); + } + + newline = 1; + p = buf; + while ((c = *p)) { + switch (c) { + case '\n': + newline = 1; + putchar(c); + p++; + break; + case '<': + if (newline && isdigit(p[1]) && p[2] == '>') { + p += 3; + break; + } + /* else fall through */ + default: + newline = 0; + putchar(c); + p++; + } + } + if (!newline) + putchar('\n'); + + return 0; +} diff --git a/usr/utils/false.c b/usr/utils/false.c new file mode 100644 index 0000000..2c3243a --- /dev/null +++ b/usr/utils/false.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 1; +} diff --git a/usr/utils/file_mode.c b/usr/utils/file_mode.c new file mode 100644 index 0000000..48f7f43 --- /dev/null +++ b/usr/utils/file_mode.c @@ -0,0 +1,143 @@ +#include <sys/stat.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "file_mode.h" + +extern char *progname; + +mode_t parse_file_mode(char *arg, mode_t mode, mode_t sumask) +{ + char *clause; + + if (isdigit(*arg) && *arg < '8') { + unsigned long num; + + num = strtoul(arg, NULL, 8); + if ((num == ULONG_MAX && errno == ERANGE) || num > 07777) { + fprintf(stderr, "%s: invalid mode `%s'\n", progname, + arg); + exit(255); + } + return (mode_t) num; + } + + while ((clause = strsep(&arg, ",")) != NULL) { + mode_t who = 0; + int action; + char *p = clause; + + /* + * Parse the who list. Optional. + */ + while (1) { + switch (*p++) { + case 'u': + who |= S_IRWXU | S_ISUID; + continue; + case 'g': + who |= S_IRWXG | S_ISGID; + continue; + case 'o': + who |= S_IRWXO | S_ISVTX; + continue; + case 'a': + who = + S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | + S_ISGID | S_ISVTX; + continue; + } + /* undo the increment above */ + p--; + break; + } + + if (who == 0) + who = (~sumask) | S_ISVTX; + + /* + * Parse an action list. Must be at least one action. + */ + while (*p) { + mode_t perm = 0; + + /* + * Parse the action + */ + action = *p; + if (action == '+' || action == '-' || action == '=') + p++; + + /* + * Parse perm + */ + while (*p) { + switch (*p++) { + case 'r': + perm |= S_IRUSR | S_IRGRP | S_IROTH; + continue; + case 'w': + perm |= S_IWUSR | S_IWGRP | S_IWOTH; + continue; + case 'x': + perm |= S_IXUSR | S_IXGRP | S_IXOTH; + continue; + case 'X': + perm |= S_ISVTX; + continue; + case 's': + perm |= S_ISUID | S_ISGID; + continue; + case 'u': + perm = mode & S_IRWXU; + perm |= perm >> 3 | perm >> 6; + if (mode & S_ISUID) + perm |= S_ISGID; + continue; + case 'g': + perm = mode & S_IRWXG; + perm |= perm << 3 | perm >> 3; + if (mode & S_ISGID) + perm |= S_ISUID; + continue; + case 'o': + perm = mode & S_IRWXO; + perm |= perm << 6 | perm << 3; + continue; + } + /* undo the increment above */ + p--; + break; + } + + perm &= who; + + switch (action) { + case '+': + mode |= perm; + continue; + + case '-': + mode &= ~perm; + continue; + + case '=': + mode &= ~who; + mode |= perm; + continue; + } + + if (!action) + break; + fprintf(stderr, "%s: invalid mode `%s'\n", progname, + clause); + exit(255); + } + } + + return mode; +} diff --git a/usr/utils/file_mode.h b/usr/utils/file_mode.h new file mode 100644 index 0000000..364f603 --- /dev/null +++ b/usr/utils/file_mode.h @@ -0,0 +1,6 @@ +#ifndef UTILS_FILE_MODE_H +#define UTILS_FILE_MODE_H + +mode_t parse_file_mode(char *arg, mode_t mode, mode_t sumask); + +#endif /* UTILS_FILE_MODE_H */ diff --git a/usr/utils/halt.c b/usr/utils/halt.c new file mode 100644 index 0000000..368f095 --- /dev/null +++ b/usr/utils/halt.c @@ -0,0 +1,64 @@ +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/reboot.h> +#include <klibc/compiler.h> + +static __noreturn usage(void) +{ + static char mesg[] = "Usage: {halt|reboot|poweroff} [-n] [reboot-arg]\n"; + write(2, mesg, sizeof(mesg) - 1); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int cmd = 0; /* initalize to shut gcc up */ + int do_sync = 1; + char *ptr, *ptr2; + char *reboot_arg = NULL; + + /* Which action (program name)? */ + ptr2 = ptr = argv[0]; + while (*ptr2) + if (*ptr2++ == '/') + ptr = ptr2; + if (*ptr == 'r') + cmd = LINUX_REBOOT_CMD_RESTART; + else if (*ptr == 'h') + cmd = LINUX_REBOOT_CMD_HALT; + else if (*ptr == 'p') + cmd = LINUX_REBOOT_CMD_POWER_OFF; + else + usage(); + + /* Walk options */ + while (*++argv) + if (**argv == '-') { + switch (*++*argv) { + case 'f': + break; /* -f assumed */ + case 'n': + do_sync = 0; + break; + default: + usage(); + } + } else if (cmd == LINUX_REBOOT_CMD_RESTART) { + reboot_arg = *argv; + cmd = LINUX_REBOOT_CMD_RESTART2; + } else { + usage(); /* args, not reboot == error */ + } + + if (do_sync) + sync(); + reboot(LINUX_REBOOT_CMD_CAD_OFF, NULL); /* Enable CTRL+ALT+DEL */ + if (!reboot(cmd, reboot_arg)) { + /* Success. Currently, CMD_HALT returns, so stop the world */ + /* kill(-1, SIGSTOP); */ + kill(getpid(), SIGSTOP); + } + write(2, "failed.\n", 8); + return 1; +} diff --git a/usr/utils/insmod.c b/usr/utils/insmod.c new file mode 100644 index 0000000..47b5880 --- /dev/null +++ b/usr/utils/insmod.c @@ -0,0 +1,140 @@ +/* insmod.c: insert a module into the kernel. + Copyright (C) 2001 Rusty Russell. + Copyright (C) 2002 Rusty Russell, IBM Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#define streq(a,b) (strcmp((a),(b)) == 0) + +/* This really needs to be in a header file... */ +extern long init_module(void *, unsigned long, const char *); + +static void print_usage(const char *progname) +{ + fprintf(stderr, "Usage: %s filename [args]\n", progname); + exit(1); +} + +/* We use error numbers in a loose translation... */ +static const char *moderror(int err) +{ + switch (err) { + case ENOEXEC: + return "Invalid module format"; + case ENOENT: + return "Unknown symbol in module"; + case ESRCH: + return "Module has wrong symbol version"; + case EINVAL: + return "Invalid parameters"; + default: + return strerror(err); + } +} + +static void *grab_file(const char *filename, unsigned long *size) +{ + unsigned int max = 16384; + int ret, fd; + void *buffer = malloc(max); + + if (streq(filename, "-")) + fd = dup(STDIN_FILENO); + else + fd = open(filename, O_RDONLY, 0); + + if (fd < 0) + return NULL; + + *size = 0; + while ((ret = read(fd, buffer + *size, max - *size)) > 0) { + *size += ret; + if (*size == max) + buffer = realloc(buffer, max *= 2); + } + if (ret < 0) { + free(buffer); + buffer = NULL; + } + close(fd); + return buffer; +} + +int main(int argc, char *argv[]) +{ + int i; + long int ret; + unsigned long len; + void *file; + char *filename, *options = strdup(""); + char *progname = argv[0]; + + if (argv[1] && (streq(argv[1], "--version") || streq(argv[1], "-V"))) { + puts("klibc insmod"); + exit(0); + } + + /* Ignore old options, for backwards compat. */ + while (argv[1] && (streq(argv[1], "-p") + || streq(argv[1], "-s") + || streq(argv[1], "-f"))) { + argv++; + argc--; + } + + filename = argv[1]; + if (!filename) + print_usage(progname); + + /* Rest is options */ + for (i = 2; i < argc; i++) { + options = realloc(options, + strlen(options) + 2 + strlen(argv[i]) + 2); + /* Spaces handled by "" pairs, but no way of escaping + quotes */ + if (strchr(argv[i], ' ')) + strcat(options, "\""); + strcat(options, argv[i]); + if (strchr(argv[i], ' ')) + strcat(options, "\""); + strcat(options, " "); + } + + file = grab_file(filename, &len); + if (!file) { + fprintf(stderr, "insmod: can't read '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + ret = init_module(file, len, options); + if (ret != 0) { + fprintf(stderr, "insmod: error inserting '%s': %li %s\n", + filename, ret, moderror(errno)); + exit(1); + } + exit(0); +} diff --git a/usr/utils/kill.c b/usr/utils/kill.c new file mode 100644 index 0000000..188f1b5 --- /dev/null +++ b/usr/utils/kill.c @@ -0,0 +1,33 @@ +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> + +char *progname; + +static __noreturn usage(void) +{ + fprintf(stderr, "Usage: %s pid\n", progname); + exit(1); +} +int main(int argc, char *argv[]) +{ + long pid; + char *endp; + + progname = argv[0]; + if (argc != 2) + usage(); + + pid = strtol(argv[1], &endp, 10); + if (*endp != '\0') { + perror("pid"); + usage(); + } + + if (kill(pid, SIGTERM) == -1) { + perror("kill"); + exit(-1); + } + exit(0); +} diff --git a/usr/utils/ln.c b/usr/utils/ln.c new file mode 100644 index 0000000..e826eb8 --- /dev/null +++ b/usr/utils/ln.c @@ -0,0 +1,77 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <linux/limits.h> + +int main(int argc, char *argv[]) +{ + int c, s, f; + char *p; + struct stat sb; + + s = f = 0; + do { + c = getopt(argc, argv, "sf"); + if (c == EOF) + break; + + switch (c) { + + case 's': + s = 1; + break; + case 'f': + f = 1; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + return 1; + } + + } while (1); + + if (optind == argc) { + fprintf(stderr, "Usage: %s [-s] [-f] target link\n", argv[0]); + return 1; + } + + memset(&sb, 0, sizeof(struct stat)); + if (stat(argv[argc - 1], &sb) < 0 && argc - optind > 2) { + if (!(S_ISDIR(sb.st_mode))) { + fprintf(stderr, + "multiple targets and %s is not a directory\n", + argv[argc - 1]); + return 1; + } + } + + for (c = optind; c < argc - 1; c++) { + char target[PATH_MAX]; + + p = strrchr(argv[c], '/'); + p++; + + if (S_ISDIR(sb.st_mode)) + snprintf(target, PATH_MAX, "%s/%s", argv[argc - 1], p); + else + snprintf(target, PATH_MAX, "%s", argv[argc - 1]); + + if (f) + unlink(target); + + if (s) { + if (symlink(argv[c], target) == -1) + perror(target); + } else { + if (link(argv[c], target) == -1) + perror(target); + } + } + + return 0; +} diff --git a/usr/utils/losetup.c b/usr/utils/losetup.c new file mode 100644 index 0000000..16159eb --- /dev/null +++ b/usr/utils/losetup.c @@ -0,0 +1,400 @@ +/* Originally from Ted's losetup.c */ + +#define LOOPMAJOR 7 + +/* + * losetup.c - setup and control loop devices + */ + +/* We want __u64 to be unsigned long long */ +#define __SANE_USERSPACE_TYPES__ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/sysmacros.h> +#include <stdarg.h> +#include <linux/loop.h> + +extern int verbose; +extern char *progname; +extern char *xstrdup (const char *s); /* not: #include "sundries.h" */ +extern void error (const char *fmt, ...); /* idem */ + +/* caller guarantees n > 0 */ +void xstrncpy(char *dest, const char *src, size_t n) +{ + strncpy(dest, src, n-1); + dest[n-1] = 0; +} + + +static int show_loop(char *device) +{ + struct loop_info64 loopinfo64; + int fd, errsv; + + if ((fd = open(device, O_RDONLY)) < 0) { + int errsv = errno; + fprintf(stderr, "loop: can't open device %s: %s\n", + device, strerror (errsv)); + return 2; + } + + if (ioctl(fd, LOOP_GET_STATUS64, &loopinfo64) == 0) { + + loopinfo64.lo_file_name[LO_NAME_SIZE-2] = '*'; + loopinfo64.lo_file_name[LO_NAME_SIZE-1] = 0; + loopinfo64.lo_crypt_name[LO_NAME_SIZE-1] = 0; + + printf("%s: [%04llx]:%llu (%s)", + device, loopinfo64.lo_device, loopinfo64.lo_inode, + loopinfo64.lo_file_name); + + if (loopinfo64.lo_offset) + printf(", offset %lld", loopinfo64.lo_offset); + + if (loopinfo64.lo_sizelimit) + printf(", sizelimit %lld", loopinfo64.lo_sizelimit); + + if (loopinfo64.lo_encrypt_type || + loopinfo64.lo_crypt_name[0]) { + const char *e = (const char *)loopinfo64.lo_crypt_name; + + if (*e == 0 && loopinfo64.lo_encrypt_type == 1) + e = "XOR"; + printf(", encryption %s (type %d)", + e, loopinfo64.lo_encrypt_type); + } + printf("\n"); + close (fd); + return 0; + } + + errsv = errno; + fprintf(stderr, "loop: can't get info on device %s: %s\n", + device, strerror (errsv)); + close (fd); + return 1; +} + +int +is_loop_device (const char *device) { + struct stat statbuf; + + return (stat(device, &statbuf) == 0 && + S_ISBLK(statbuf.st_mode) && + major(statbuf.st_rdev) == LOOPMAJOR); +} + +#define SIZE(a) (sizeof(a)/sizeof(a[0])) + +char * find_unused_loop_device (void) +{ + char dev[20]; + int fd, rc; + + fd = open("/dev/loop-control", O_RDWR); + if (fd < 0) { + error("%s: could not open /dev/loop-control. Maybe this kernel " + "does not know\n" + " about the loop device? (If so, recompile or " + "`modprobe loop'.)", progname); + return NULL; + } + rc = ioctl(fd, LOOP_CTL_GET_FREE, 0); + close(fd); + if (rc < 0) { + error("%s: could not find any free loop device", progname); + return NULL; + } + + sprintf(dev, "/dev/loop%d", rc); + return xstrdup(dev); +} + +/* + * A function to read the passphrase either from the terminal or from + * an open file descriptor. + */ +static char * xgetpass(int pfd, const char *prompt) +{ + char *pass; + int buflen, i; + + pass = NULL; + buflen = 0; + for (i=0; ; i++) { + if (i >= buflen-1) { + /* we're running out of space in the buffer. + * Make it bigger: */ + char *tmppass = pass; + buflen += 128; + pass = realloc(tmppass, buflen); + if (pass == NULL) { + /* realloc failed. Stop reading. */ + error("Out of memory while reading passphrase"); + pass = tmppass; /* the old buffer hasn't changed */ + break; + } + } + if (read(pfd, pass+i, 1) != 1 || + pass[i] == '\n' || pass[i] == 0) + break; + } + + if (pass == NULL) + return ""; + + pass[i] = 0; + return pass; +} + +static int digits_only(const char *s) +{ + while (*s) + if (!isdigit(*s++)) + return 0; + return 1; +} + +int set_loop(const char *device, const char *file, unsigned long long offset, + const char *encryption, int pfd, int *loopro) { + struct loop_info64 loopinfo64; + int fd, ffd, mode, i; + char *pass; + + mode = (*loopro ? O_RDONLY : O_RDWR); + if ((ffd = open(file, mode)) < 0) { + if (!*loopro && errno == EROFS) + ffd = open(file, mode = O_RDONLY); + if (ffd < 0) { + perror(file); + return 1; + } + } + if ((fd = open(device, mode)) < 0) { + perror (device); + return 1; + } + *loopro = (mode == O_RDONLY); + + memset(&loopinfo64, 0, sizeof(loopinfo64)); + + xstrncpy((char *)loopinfo64.lo_file_name, file, LO_NAME_SIZE); + + if (encryption && *encryption) { + if (digits_only(encryption)) { + loopinfo64.lo_encrypt_type = atoi(encryption); + } else { + loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI; + snprintf((char *)loopinfo64.lo_crypt_name, LO_NAME_SIZE, + "%s", encryption); + } + } + + loopinfo64.lo_offset = offset; + + + switch (loopinfo64.lo_encrypt_type) { + case LO_CRYPT_NONE: + loopinfo64.lo_encrypt_key_size = 0; + break; + case LO_CRYPT_XOR: + pass = xgetpass(pfd, "Password: "); + goto gotpass; + default: + pass = xgetpass(pfd, "Password: "); + gotpass: + memset(loopinfo64.lo_encrypt_key, 0, LO_KEY_SIZE); + xstrncpy((char *)loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE); + memset(pass, 0, strlen(pass)); + loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE; + } + + if (ioctl(fd, LOOP_SET_FD, (void *)(size_t)ffd) < 0) { + perror("ioctl: LOOP_SET_FD"); + return 1; + } + close (ffd); + + i = ioctl(fd, LOOP_SET_STATUS64, &loopinfo64); + if (i) + perror("ioctl: LOOP_SET_STATUS64"); + memset(&loopinfo64, 0, sizeof(loopinfo64)); + + if (i) { + ioctl (fd, LOOP_CLR_FD, 0); + close (fd); + return 1; + } + close (fd); + + if (verbose > 1) + printf("set_loop(%s,%s,%llu): success\n", + device, file, offset); + return 0; +} + +int del_loop (const char *device) +{ + int fd; + + if ((fd = open (device, O_RDONLY)) < 0) { + int errsv = errno; + fprintf(stderr, "loop: can't delete device %s: %s\n", + device, strerror (errsv)); + return 1; + } + if (ioctl (fd, LOOP_CLR_FD, 0) < 0) { + perror ("ioctl: LOOP_CLR_FD"); + close (fd); + return 1; + } + close (fd); + if (verbose > 1) + printf("del_loop(%s): success\n", device); + return 0; +} + + +int verbose = 0; +char *progname; + +static void usage(FILE *f) +{ + fprintf(f, "usage:\n\ + %s loop_device # give info\n\ + %s -d loop_device # delete\n\ + %s -f # find unused\n\ + %s -h # this help\n\ + %s [-e encryption] [-o offset] {-f|loop_device} file # setup\n", + progname, progname, progname, progname, progname); + exit(f == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +char * xstrdup (const char *s) { + char *t; + + if (s == NULL) + return NULL; + + t = strdup (s); + + if (t == NULL) { + fprintf(stderr, "not enough memory"); + exit(1); + } + + return t; +} + +void error (const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vfprintf (stderr, fmt, args); + va_end (args); + fprintf (stderr, "\n"); +} + +int main(int argc, char **argv) +{ + char *p, *offset, *encryption, *passfd, *device, *file; + int delete, find, c; + int res = 0; + int ro = 0; + int pfd = -1; + unsigned long long off; + + + delete = find = 0; + off = 0; + offset = encryption = passfd = NULL; + + progname = argv[0]; + if ((p = strrchr(progname, '/')) != NULL) + progname = p+1; + + while ((c = getopt(argc, argv, "de:E:fho:p:v")) != -1) { + switch (c) { + case 'd': + delete = 1; + break; + case 'E': + case 'e': + encryption = optarg; + break; + case 'f': + find = 1; + break; + case 'h': + usage(stdout); + break; + case 'o': + offset = optarg; + break; + case 'p': + passfd = optarg; + break; + case 'v': + verbose = 1; + break; + default: + usage(stderr); + } + } + + if (argc == 1) { + usage(stderr); + } else if (delete) { + if (argc != optind+1 || encryption || offset || find) + usage(stderr); + } else if (find) { + if (argc < optind || argc > optind+1) + usage(stderr); + } else { + if (argc < optind+1 || argc > optind+2) + usage(stderr); + } + + if (find) { + device = find_unused_loop_device(); + if (device == NULL) + return -1; + if (verbose) + printf("Loop device is %s\n", device); + if (argc == optind) { + printf("%s\n", device); + return 0; + } + file = argv[optind]; + } else { + device = argv[optind]; + if (argc == optind+1) + file = NULL; + else + file = argv[optind+1]; + } + + if (delete) + res = del_loop(device); + else if (file == NULL) + res = show_loop(device); + else { + if (offset && sscanf(offset, "%llu", &off) != 1) + usage(stderr); + if (passfd && sscanf(passfd, "%d", &pfd) != 1) + usage(stderr); + res = set_loop(device, file, off, encryption, pfd, &ro); + } + return res; +} diff --git a/usr/utils/ls.c b/usr/utils/ls.c new file mode 100644 index 0000000..50af434 --- /dev/null +++ b/usr/utils/ls.c @@ -0,0 +1,223 @@ +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/sysmacros.h> + +#define STAT_ISSET(mode, mask) (((mode) & mask) == mask) + +static size_t max_linksiz = 128; +static int max_nlinks = 1; +static int max_size = 1; +static int max_uid = 1; +static int max_gid = 1; +static int max_min = 1; +static int max_maj = 1; + +static void do_preformat(const struct stat *st) +{ + int bytes; + + bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_nlink); + if (bytes > max_nlinks) + max_nlinks = bytes; + + bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_uid); + if (bytes > max_uid) + max_uid = bytes; + + bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_gid); + if (bytes > max_gid) + max_gid = bytes; + + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + bytes = snprintf(NULL, 0, "%u", major(st->st_rdev)); + if (bytes > max_maj) + max_maj = bytes; + + bytes = snprintf(NULL, 0, "%u", minor(st->st_rdev)); + if (bytes > max_min) + max_min = bytes; + + max_size = max_maj + max_min + 1; + } else { + bytes = snprintf(NULL, 0, "%ju", (uintmax_t) st->st_size); + if (bytes > max_size) + max_size = bytes; + } + return; +} + +static void do_stat(const struct stat *st, int dir_fd, const char *path) +{ + char *fmt, *link_name; + int rc; + + switch (st->st_mode & S_IFMT) { + case S_IFBLK: + putchar('b'); + break; + case S_IFCHR: + putchar('c'); + break; + case S_IFDIR: + putchar('d'); + break; + case S_IFIFO: + putchar('p'); + break; + case S_IFLNK: + putchar('l'); + break; + case S_IFSOCK: + putchar('s'); + break; + case S_IFREG: + putchar('-'); + break; + default: + putchar('?'); + break; + } + putchar(STAT_ISSET(st->st_mode, S_IRUSR) ? 'r' : '-'); + putchar(STAT_ISSET(st->st_mode, S_IWUSR) ? 'w' : '-'); + + !STAT_ISSET(st->st_mode, S_ISUID) ? + putchar(STAT_ISSET(st->st_mode, S_IXUSR) ? 'x' : '-') : + putchar('S'); + + putchar(STAT_ISSET(st->st_mode, S_IRGRP) ? 'r' : '-'); + putchar(STAT_ISSET(st->st_mode, S_IWGRP) ? 'w' : '-'); + + !STAT_ISSET(st->st_mode, S_ISGID) ? + putchar(STAT_ISSET(st->st_mode, S_IXGRP) ? 'x' : '-') : + putchar('S'); + + putchar(STAT_ISSET(st->st_mode, S_IROTH) ? 'r' : '-'); + putchar(STAT_ISSET(st->st_mode, S_IWOTH) ? 'w' : '-'); + + !STAT_ISSET(st->st_mode, S_ISVTX) ? + putchar(STAT_ISSET(st->st_mode, S_IXOTH) ? 'x' : '-') : + putchar(S_ISDIR(st->st_mode) ? 't' : 'T'); + + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + rc = asprintf(&fmt, " %%%dju %%%dju %%%dju %%%du,%%%du %%s", + max_nlinks, max_uid, max_gid, max_maj, max_min); + if (rc == -1) { + perror("asprintf"); + exit(1); + } + fprintf(stdout, fmt, + (uintmax_t) st->st_nlink, + (uintmax_t) st->st_uid, + (uintmax_t) st->st_gid, + major(st->st_rdev), + minor(st->st_rdev), + path); + } else { + rc = asprintf(&fmt, " %%%dju %%%dju %%%dju %%%dju %%s", + max_nlinks, max_uid, max_gid, max_size); + if (rc == -1) { + perror("asprintf"); + exit(1); + } + fprintf(stdout, fmt, + (uintmax_t) st->st_nlink, + (uintmax_t) st->st_uid, + (uintmax_t) st->st_gid, + (uintmax_t) st->st_size, + path); + } + free(fmt); + + if (S_ISLNK(st->st_mode)) { + link_name = malloc(max_linksiz); + if (link_name == NULL) { + perror("malloc"); + exit(1); + } + rc = readlinkat(dir_fd, path, link_name, max_linksiz); + if (rc == -1) { + free(link_name); + perror("readlink"); + exit(1); + } + link_name[rc] = '\0'; + fprintf(stdout, " -> %s", link_name); + free(link_name); + } + + putchar('\n'); + return; +} + +static void do_dir(const char *path, int preformat) +{ + DIR *dir; + int dir_fd; + struct dirent *dent; + struct stat st; + + dir = opendir(path); + if (dir == NULL) { + perror(path); + exit(1); + } + dir_fd = dirfd(dir); + + while ((dent = readdir(dir)) != NULL) { + if (fstatat(dir_fd, dent->d_name, &st, + AT_SYMLINK_NOFOLLOW)) { + perror(dent->d_name); + exit(1); + } + (preformat) ? + do_preformat(&st) : + do_stat(&st, dir_fd, dent->d_name); + } + + closedir(dir); +} + +int main(int argc, char *argv[]) +{ + int i; + struct stat st; + + if (argc == 1) { + do_dir(".", 1); + do_dir(".", 0); + return 0; + } + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == 'h') { + fprintf(stdout, "Usage: ls [-h] [FILE ...]\n"); + return 0; + } + + if (lstat(argv[i], &st)) { + perror(argv[i]); + exit(1); + } + + S_ISDIR(st.st_mode) ? + do_dir(argv[i], 1) : + do_preformat(&st); + } + + for (i = 1; i < argc; i++) { + if (lstat(argv[i], &st)) { + perror(argv[i]); + exit(1); + } + + S_ISDIR(st.st_mode) ? + do_dir(argv[i], 0) : + do_stat(&st, AT_FDCWD, argv[i]); + } + + return 0; +} diff --git a/usr/utils/minips.c b/usr/utils/minips.c new file mode 100644 index 0000000..f48505f --- /dev/null +++ b/usr/utils/minips.c @@ -0,0 +1,511 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* This is a minimal /bin/ps, designed to be smaller than the old ps + * while still supporting some of the more important features of the + * new ps. (for total size, note that this ps does not need libproc) + * It is suitable for Linux-on-a-floppy systems only. + * + * Maintainers: do not compile or install for normal systems. + * Anyone needing this will want to tweak their compiler anyway. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> + +#include <asm/param.h> /* HZ */ + +static int P_euid; +static int P_pid; +static char P_cmd[16]; +static char P_state; +static int P_ppid, P_pgrp, P_session, P_tty, P_tpgid; +static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, + P_utime, P_stime; +static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_it_real_value; +static unsigned long P_start_time, P_vsize; +static long P_rss; +static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, + P_kstk_esp, P_kstk_eip; +static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch; +static unsigned long P_wchan, P_nswap, P_cnswap; + +#if 0 +static int screen_cols = 80; +static int w_count; +#endif + +static int want_one_pid; +static const char *want_one_command; +static int select_notty; +static int select_all; + +static int ps_format; +static int old_h_option; + +/* we only pretend to support this */ +static int show_args; /* implicit with -f and all BSD options */ +static int bsd_c_option; /* this option overrides the above */ + +static int ps_argc; /* global argc */ +static char **ps_argv; /* global argv */ +static int thisarg; /* index into ps_argv */ +static char *flagptr; /* current location in ps_argv[thisarg] */ + +#ifndef HZ +#warning HZ not defined, assuming it is 100 +#define HZ 100 +#endif + +int page_shift; /* Page size as shift count */ + +static void usage(void) +{ + fprintf(stderr, + "-C select by command name (minimal ps only accepts one)\n" + "-p select by process ID (minimal ps only accepts one)\n" + "-e all processes (same as ax)\n" + "a all processes w/ tty, including other users\n" + "x processes w/o controlling ttys\n" + "-f full format\n" + "-j,j job control format\n" + "v virtual memory format\n" + "-l,l long format\n" + "u user-oriented format\n" + "-o user-defined format (limited support, only \"ps -o pid=\")\n" + "h no header\n" +/* + "-A all processes (same as ax)\n" + "c true command name\n" + "-w,w wide output\n" +*/ + ); + exit(1); +} + +/* + * Return the next argument, or call the usage function. + * This handles both: -oFOO -o FOO + */ +static const char *get_opt_arg(void) +{ + const char *ret; + ret = flagptr + 1; /* assume argument is part of ps_argv[thisarg] */ + if (*ret) + return ret; + if (++thisarg >= ps_argc) + usage(); /* there is nothing left */ + /* argument is the new ps_argv[thisarg] */ + ret = ps_argv[thisarg]; + if (!ret || !*ret) + usage(); + return ret; +} + +/* return the PID, or 0 if nothing good */ +static void parse_pid(const char *str) +{ + char *endp; + int num; + if (!str) + goto bad; + num = strtol(str, &endp, 0); + if (*endp != '\0') + goto bad; + if (num < 1) + goto bad; + if (want_one_pid) + goto bad; + want_one_pid = num; + return; + bad: + usage(); +} + +/***************** parse SysV options, including Unix98 *****************/ +static void parse_sysv_option(void) +{ + do { + switch (*flagptr) { + /**** selection ****/ + case 'C': /* end */ + if (want_one_command) + usage(); + want_one_command = get_opt_arg(); + return; /* can't have any more options */ + case 'p': /* end */ + parse_pid(get_opt_arg()); + return; /* can't have any more options */ + case 'A': + case 'e': + select_all++; + select_notty++; + case 'w': /* here for now, since the real one is not used */ + break; + /**** output format ****/ + case 'f': + show_args = 1; + /* FALL THROUGH */ + case 'j': + case 'l': + if (ps_format) + usage(); + ps_format = *flagptr; + break; + case 'o': /* end */ + /* We only support a limited form: "ps -o pid=" (yes, just "pid=") */ + if (strcmp(get_opt_arg(), "pid=")) + usage(); + if (ps_format) + usage(); + ps_format = 'o'; + old_h_option++; + return; /* can't have any more options */ + /**** other stuff ****/ +#if 0 + case 'w': + w_count++; + break; +#endif + default: + usage(); + } /* switch */ + } while (*++flagptr); +} + +/************************* parse BSD options **********************/ +static void parse_bsd_option(void) +{ + do { + switch (*flagptr) { + /**** selection ****/ + case 'a': + select_all++; + break; + case 'x': + select_notty++; + break; + case 'p': /* end */ + parse_pid(get_opt_arg()); + return; /* can't have any more options */ + /**** output format ****/ + case 'j': + case 'l': + case 'u': + case 'v': + if (ps_format) + usage(); + ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */ + break; + /**** other stuff ****/ + case 'c': + bsd_c_option++; +#if 0 + break; +#endif + case 'w': +#if 0 + w_count++; +#endif + break; + case 'h': + old_h_option++; + break; + default: + usage(); + } /* switch */ + } while (*++flagptr); +} + +#if 0 +/* not used yet */ +static void choose_dimensions(void) +{ + struct winsize ws; + char *columns; + /* screen_cols is 80 by default */ + if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 30) + screen_cols = ws.ws_col; + columns = getenv("COLUMNS"); + if (columns && *columns) { + long t; + char *endptr; + t = strtol(columns, &endptr, 0); + if (!*endptr && (t > 30) && (t < (long)999999999)) + screen_cols = (int)t; + } + if (w_count && (screen_cols < 132)) + screen_cols = 132; + if (w_count > 1) + screen_cols = 999999999; +} +#endif + +static void arg_parse(int argc, char *argv[]) +{ + int sel = 0; /* to verify option sanity */ + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + /**** iterate over the args ****/ + while (++thisarg < ps_argc) { + flagptr = ps_argv[thisarg]; + switch (*flagptr) { + case '0'...'9': + show_args = 1; + parse_pid(flagptr); + break; + case '-': + flagptr++; + parse_sysv_option(); + break; + default: + show_args = 1; + parse_bsd_option(); + break; + } + } + /**** sanity check and clean-up ****/ + if (want_one_pid) + sel++; + if (want_one_command) + sel++; + if (select_notty || select_all) + sel++; + if (sel > 1 || select_notty > 1 || select_all > 1 || bsd_c_option > 1 + || old_h_option > 1) + usage(); + if (bsd_c_option) + show_args = 0; +} + +/* return 1 if it works, or 0 for failure */ +static int stat2proc(int pid) +{ + char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */ + int num; + int fd; + char *tmp; + struct stat sb; /* stat() used to get EUID */ + + snprintf(buf, 32, "/proc/%d/stat", pid); + fd = open(buf, O_RDONLY, 0); + if (fd == -1) + return 0; + num = read(fd, buf, sizeof buf - 1); + fstat(fd, &sb); + P_euid = sb.st_uid; + close(fd); + if (num < 80) + return 0; + buf[num] = '\0'; + tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */ + *tmp = '\0'; /* replace trailing ')' with NUL */ + /* parse these two strings separately, skipping the leading "(". */ + memset(P_cmd, 0, sizeof P_cmd); /* clear */ + sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */ + num = sscanf(tmp + 2, /* skip space after ')' too */ + "%c " "%d %d %d %d %d " "%lu %lu %lu %lu %lu %lu %lu " "%ld %ld %ld %ld %ld %ld " "%lu %lu " "%ld " "%lu %lu %lu %lu %lu %lu " "%u %u %u %u " /* no use for RT signals */ + "%lu %lu %lu", + &P_state, + &P_ppid, &P_pgrp, &P_session, &P_tty, &P_tpgid, + &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, + &P_utime, &P_stime, &P_cutime, &P_cstime, &P_priority, + &P_nice, &P_timeout, &P_it_real_value, &P_start_time, + &P_vsize, &P_rss, &P_rss_rlim, &P_start_code, &P_end_code, + &P_start_stack, &P_kstk_esp, &P_kstk_eip, &P_signal, + &P_blocked, &P_sigignore, &P_sigcatch, &P_wchan, &P_nswap, + &P_cnswap); +/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ + P_vsize /= 1024; + P_rss <<= page_shift - 10; + if (num < 30) + return 0; + if (P_pid != pid) + return 0; + return 1; +} + +static const char *do_time(unsigned long t) +{ + int hh, mm, ss; + static char buf[32]; + int cnt = 0; + t /= HZ; + ss = t % 60; + t /= 60; + mm = t % 60; + t /= 60; + hh = t % 24; + t /= 24; + if (t) + cnt = snprintf(buf, sizeof buf, "%d-", (int)t); + snprintf(cnt + buf, sizeof(buf) - cnt, "%02d:%02d:%02d", hh, mm, ss); + return buf; +} + +static void print_proc(void) +{ + char tty[16]; + snprintf(tty, sizeof tty, "%3d,%-3d", (P_tty >> 8) & 0xff, + P_tty & 0xff); + switch (ps_format) { + case 0: + printf("%5d %s %s", P_pid, tty, do_time(P_utime + P_stime)); + break; + case 'o': + printf("%d\n", P_pid); + return; /* don't want the command */ + case 'l': + printf("%03x %c %5d %5d %5d - %3d %3d - " + "%5ld %06x %s %s", + (unsigned)P_flags & 0x777, P_state, P_euid, P_pid, + P_ppid, (int)P_priority, (int)P_nice, + P_vsize >> (page_shift - 10), + (unsigned)(P_wchan & 0xffffff), tty, + do_time(P_utime + P_stime) + ); + break; + case 'f': + printf("%5d %5d %5d - - %s %s", + P_euid, P_pid, P_ppid, tty, do_time(P_utime + P_stime) + ); + break; + case 'j': + printf("%5d %5d %5d %s %s", + P_pid, P_pgrp, P_session, tty, do_time(P_utime + P_stime) + ); + break; + case 'u' | 0x80: + printf("%5d %5d - - %5ld %5ld %s %c - %s", + P_euid, P_pid, P_vsize, P_rss, tty, P_state, + do_time(P_utime + P_stime) + ); + break; + case 'v' | 0x80: + printf("%5d %s %c %s %6d - - %5d -", + P_pid, tty, P_state, do_time(P_utime + P_stime), + (int)P_maj_flt, (int)P_rss); + break; + case 'j' | 0x80: + printf("%5d %5d %5d %5d %s %5d %c %5d %s", + P_ppid, P_pid, P_pgrp, P_session, tty, P_tpgid, P_state, + P_euid, do_time(P_utime + P_stime) + ); + break; + case 'l' | 0x80: + printf("%03x %5d %5d %5d %3d %3d " + "%5ld %4ld %06x %c %s %s", + (unsigned)P_flags & 0x777, P_euid, P_pid, P_ppid, + (int)P_priority, (int)P_nice, P_vsize, P_rss, + (unsigned)(P_wchan & 0xffffff), P_state, tty, + do_time(P_utime + P_stime) + ); + break; + default: + break; + } + if (show_args) + printf(" [%s]\n", P_cmd); + else + printf(" %s\n", P_cmd); +} + +int main(int argc, char *argv[]) +{ + arg_parse(argc, argv); + + page_shift = __getpageshift(); + +#if 0 + choose_dimensions(); +#endif + if (!old_h_option) { + const char *head; + switch (ps_format) { + default: /* can't happen */ + case 0: + head = " PID TTY TIME CMD"; + break; + case 'l': + head = + " F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; + break; + case 'f': + head = + " UID PID PPID C STIME TTY TIME CMD"; + break; + case 'j': + head = " PID PGID SID TTY TIME CMD"; + break; + case 'u' | 0x80: + head = + " UID PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; + break; + case 'v' | 0x80: + head = + " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; + break; + case 'j' | 0x80: + head = + " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; + break; + case 'l' | 0x80: + head = + " F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; + break; + } + printf("%s\n", head); + } + if (want_one_pid) { + if (stat2proc(want_one_pid)) + print_proc(); + else + exit(1); + } else { + struct dirent *ent; /* dirent handle */ + DIR *dir; + int ouruid; + int found_a_proc; + found_a_proc = 0; + ouruid = getuid(); + dir = opendir("/proc"); + if (!dir) + exit(1); + while ((ent = readdir(dir))) { + if (*ent->d_name < '0' || *ent->d_name > '9') + continue; + if (!stat2proc(atoi(ent->d_name))) + continue; + if (want_one_command) { + if (strcmp(want_one_command, P_cmd)) + continue; + } else { + if (!select_notty && P_tty == -1) + continue; + if (!select_all && P_euid != ouruid) + continue; + } + found_a_proc++; + print_proc(); + } + closedir(dir); + exit(!found_a_proc); + } + return 0; +} diff --git a/usr/utils/mkdir.c b/usr/utils/mkdir.c new file mode 100644 index 0000000..af241ef --- /dev/null +++ b/usr/utils/mkdir.c @@ -0,0 +1,154 @@ +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "file_mode.h" + +static mode_t leaf_mode, subdir_mode; +static int p_flag; + +char *progname; + +static __noreturn usage(void) +{ + fprintf(stderr, "Usage: %s [-p] [-m mode] dir...\n", progname); + exit(1); +} + +static int make_one_dir(char *dir, mode_t mode) +{ + struct stat stbuf; + + if (mkdir(dir, mode) == -1) { + int err = errno; + + /* + * Ignore the error if it all of the following + * are satisfied: + * - error was EEXIST + * - -p was specified + * - stat indicates that its a directory + */ + if (p_flag && errno == EEXIST && + stat(dir, &stbuf) == 0 && S_ISDIR(stbuf.st_mode)) + return 1; + errno = err; + fprintf(stderr, "%s: ", progname); + perror(dir); + return -1; + } + return 0; +} + +static int make_dir(char *dir) +{ + int ret; + + if (p_flag) { + char *s, *p; + + /* + * Recurse each directory, trying to make it + * as we go. Should we check to see if it + * exists, and if so if it's a directory + * before calling mkdir? + */ + s = dir; + while ((p = strchr(s, '/')) != NULL) { + /* + * Ignore the leading / + */ + if (p != dir) { + *p = '\0'; + + /* + * Make the intermediary directory. POSIX + * says that these directories are created + * with umask,u+wx + */ + if (make_one_dir(dir, subdir_mode) == -1) + return -1; + + *p = '/'; + } + s = p + 1; + } + } + + /* + * Make the final target. Only complain if the + * target already exists if -p was not specified. + * This is created with the asked for mode & ~umask + */ + ret = make_one_dir(dir, leaf_mode); + if (ret == -1) + return -1; + + /* + * We might not set all the permission bits. Do that + * here (but only if we did create it.) + */ + if (ret == 0 && chmod(dir, leaf_mode) == -1) { + int err_save = errno; + + /* + * We failed, remove the directory we created + */ + rmdir(dir); + errno = err_save; + fprintf(stderr, "%s: ", progname); + perror(dir); + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int c, ret = 0; + mode_t saved_umask; + + progname = argv[0]; + + saved_umask = umask(0); + leaf_mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~saved_umask; + subdir_mode = (saved_umask ^ (S_IRWXU | S_IRWXG | S_IRWXO)) + | S_IWUSR | S_IXUSR; + + do { + c = getopt(argc, argv, "pm:"); + if (c == EOF) + break; + switch (c) { + case 'm': + leaf_mode = + parse_file_mode(optarg, leaf_mode, saved_umask); + break; + case 'p': + p_flag = 1; + break; + + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + usage(); + } + } while (1); + + if (optind == argc) + usage(); + + while (optind < argc) { + if (make_dir(argv[optind])) + ret = 255; /* seems to be what gnu mkdir does */ + optind++; + } + + return ret; +} diff --git a/usr/utils/mkfifo.c b/usr/utils/mkfifo.c new file mode 100644 index 0000000..5a758b2 --- /dev/null +++ b/usr/utils/mkfifo.c @@ -0,0 +1,70 @@ +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "file_mode.h" + +static mode_t leaf_mode; + +char *progname; + +static int make_fifo(char *dir) +{ + if (mkfifo(dir, leaf_mode)) { + /* + * We failed, remove the directory we created. + */ + fprintf(stderr, "%s: ", progname); + perror(dir); + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int c, ret = 0; + mode_t saved_umask; + + progname = argv[0]; + + saved_umask = umask(0); + leaf_mode = + (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) & + ~saved_umask; + + do { + c = getopt(argc, argv, "m:"); + if (c == EOF) + break; + switch (c) { + case 'm': + leaf_mode = + parse_file_mode(optarg, leaf_mode, saved_umask); + break; + + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + exit(1); + } + } while (1); + + if (optind == argc) { + fprintf(stderr, "Usage: %s [-m mode] file...\n", progname); + exit(1); + } + + while (optind < argc) { + if (make_fifo(argv[optind])) + ret = 255; /* seems to be what gnu mkdir does */ + optind++; + } + + return ret; +} diff --git a/usr/utils/mknod.c b/usr/utils/mknod.c new file mode 100644 index 0000000..fa7ac7a --- /dev/null +++ b/usr/utils/mknod.c @@ -0,0 +1,84 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +char *progname; + +static __noreturn usage(void) +{ + fprintf(stderr, "Usage: %s [-m mode] name {b|c|p} major minor\n", + progname); + exit(1); +} + +int main(int argc, char *argv[]) +{ + char *name, *type, typec, *endp; + unsigned int major_num, minor_num; + mode_t mode, mode_set = 0; + dev_t dev; + + progname = *argv++; + if (argc == 1) + usage(); + + if (argv[0][0] == '-' && argv[0][1] == 'm' && !argv[0][2]) { + mode_set = strtoul(argv[1], &endp, 8); + argv += 2; + } + + name = *argv++; + if (!name) + usage(); + + type = *argv++; + if (!type || !type[0] || type[1]) + usage(); + typec = type[0]; + + mode = 0; + switch (typec) { + case 'c': + mode = S_IFCHR; + break; + case 'b': + mode = S_IFBLK; + break; + case 'p': + mode = S_IFIFO; + break; + default: + usage(); + } + + if (mode == S_IFIFO) { + dev = 0; + } else { + if (!argv[0] || !argv[1]) + usage(); + + major_num = strtol(*argv++, &endp, 0); + if (*endp != '\0') + usage(); + minor_num = strtol(*argv++, &endp, 0); + if (*endp != '\0') + usage(); + dev = makedev(major_num, minor_num); + } + + if (*argv) + usage(); + + if (mknod(name, mode|0666, dev) == -1) { + perror("mknod"); + exit(1); + } + + if (mode_set && chmod(name, mode_set)) { + perror("chmod"); + exit(1); + } + + exit(0); +} diff --git a/usr/utils/mount_main.c b/usr/utils/mount_main.c new file mode 100644 index 0000000..0d299c4 --- /dev/null +++ b/usr/utils/mount_main.c @@ -0,0 +1,156 @@ +/* + * by rmk + */ +#include <sys/mount.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <mntent.h> + +#include "mount_opts.h" + +#define _PATH_MOUNTED "/etc/mtab" +#define _PATH_PROC_MOUNTS "/proc/mounts" + +char *progname; + +static struct extra_opts extra; +static unsigned long rwflag; + +static __noreturn usage(void) +{ + fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] [-f] [-i] " + "[-n] device directory\n", progname); + exit(1); +} + +static __noreturn print_mount(char *type) +{ + FILE *mfp; + struct mntent *mnt; + + mfp = setmntent(_PATH_PROC_MOUNTS, "r"); + if (!mfp) + mfp = setmntent(_PATH_MOUNTED, "r"); + if (!mfp) + perror("setmntent"); + + while ((mnt = getmntent(mfp)) != NULL) { + if (mnt->mnt_fsname && !strncmp(mnt->mnt_fsname, "no", 2)) + continue; + if (type && mnt->mnt_type && strcmp(type, mnt->mnt_type)) + continue; + printf("%s on %s", mnt->mnt_fsname, mnt->mnt_dir); + if (mnt->mnt_type != NULL && *mnt->mnt_type != '\0') + printf(" type %s", mnt->mnt_type); + if (mnt->mnt_opts != NULL && *mnt->mnt_opts != '\0') + printf(" (%s)", mnt->mnt_opts); + printf("\n"); + } + endmntent(mfp); + exit(0); +} + +static int +do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data) +{ + char *s; + int error = 0; + + while ((s = strsep(&type, ",")) != NULL) { +retry: + if (mount(dev, dir, s, rwflag, data) == -1) { + error = errno; + /* + * If the filesystem is not found, or the + * superblock is invalid, try the next. + */ + if (error == ENODEV || error == EINVAL) + continue; + + /* + * If we get EACCESS, and we're trying to + * mount readwrite and this isn't a remount, + * try read only. + */ + if (error == EACCES && + (rwflag & (MS_REMOUNT | MS_RDONLY)) == 0) { + rwflag |= MS_RDONLY; + goto retry; + } + } else { + error = 0; + } + break; + } + + if (error) { + errno = error; + perror("mount"); + return 255; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + char *type = NULL; + int c; + + progname = argv[0]; + rwflag = MS_VERBOSE; + + do { + c = getopt(argc, argv, "fhino:rt:w"); + if (c == EOF) + break; + switch (c) { + case 'f': + /* we can't edit /etc/mtab yet anyway; exit */ + exit(0); + case 'i': + /* ignore for now; no support for mount helpers */ + break; + case 'h': + usage(); + case 'n': + /* no mtab writing */ + break; + case 'o': + rwflag = parse_mount_options(optarg, rwflag, &extra); + break; + case 'r': + rwflag |= MS_RDONLY; + break; + case 't': + type = optarg; + break; + case 'w': + rwflag &= ~MS_RDONLY; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + exit(1); + } + } while (1); + + if (optind == argc) + print_mount(type); + + /* + * If remount, bind or move was specified, then we don't + * have a "type" as such. Use the dummy "none" type. + */ + if (rwflag & MS_TYPE) + type = "none"; + + if (optind + 2 != argc || type == NULL) + usage(); + + return do_mount(argv[optind], argv[optind + 1], type, rwflag, + extra.str); +} diff --git a/usr/utils/mount_opts.c b/usr/utils/mount_opts.c new file mode 100644 index 0000000..bb26c7d --- /dev/null +++ b/usr/utils/mount_opts.c @@ -0,0 +1,102 @@ +/* + * by rmk + * + * Decode mount options. + */ +#include <sys/mount.h> +#include <stdlib.h> +#include <string.h> + +#include "mount_opts.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +static const struct mount_opts options[] = { + /* name mask set noset */ + {"async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS}, + {"atime", MS_NOATIME, 0, MS_NOATIME}, + {"bind", MS_TYPE, MS_BIND, 0,}, + {"dev", MS_NODEV, 0, MS_NODEV}, + {"diratime", MS_NODIRATIME, 0, MS_NODIRATIME}, + {"dirsync", MS_DIRSYNC, MS_DIRSYNC, 0}, + {"exec", MS_NOEXEC, 0, MS_NOEXEC}, + {"move", MS_TYPE, MS_MOVE, 0}, + {"nodev", MS_NODEV, MS_NODEV, 0}, + {"noexec", MS_NOEXEC, MS_NOEXEC, 0}, + {"nosuid", MS_NOSUID, MS_NOSUID, 0}, + {"recurse", MS_REC, MS_REC, 0}, + {"remount", MS_TYPE, MS_REMOUNT, 0}, + {"ro", MS_RDONLY, MS_RDONLY, 0}, + {"rw", MS_RDONLY, 0, MS_RDONLY}, + {"suid", MS_NOSUID, 0, MS_NOSUID}, + {"sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0}, + {"verbose", MS_VERBOSE, MS_VERBOSE, 0}, +}; + +static void add_extra_option(struct extra_opts *extra, char *s) +{ + int len = strlen(s); + int newlen = extra->used_size + len; + + if (extra->str) + len++; /* +1 for ',' */ + + if (newlen >= extra->alloc_size) { + char *new; + + new = realloc(extra->str, newlen + 1); /* +1 for NUL */ + if (!new) + return; + + extra->str = new; + extra->end = extra->str + extra->used_size; + extra->alloc_size = newlen; + } + + if (extra->used_size) { + *extra->end = ','; + extra->end++; + } + strcpy(extra->end, s); + extra->used_size += len; + +} + +unsigned long +parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra) +{ + char *s; + + while ((s = strsep(&arg, ",")) != NULL) { + char *opt = s; + unsigned int i; + int res, no = s[0] == 'n' && s[1] == 'o'; + + if (no) + s += 2; + + for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) { + res = strcmp(s, options[i].str); + + if (res == 0) { + rwflag &= ~options[i].rwmask; + if (no) + rwflag |= options[i].rwnoset; + else + rwflag |= options[i].rwset; + } + if (res <= 0) + break; + } + + if (res != 0 && s[0]) { + if (!strcmp(opt, "defaults")) + rwflag &= ~(MS_RDONLY|MS_NOSUID|MS_NODEV| + MS_NOEXEC|MS_SYNCHRONOUS); + else + add_extra_option(extra, opt); + } + } + + return rwflag; +} diff --git a/usr/utils/mount_opts.h b/usr/utils/mount_opts.h new file mode 100644 index 0000000..cf47cae --- /dev/null +++ b/usr/utils/mount_opts.h @@ -0,0 +1,26 @@ +#ifndef UTILS_MOUNT_OPTS_H +#define UTILS_MOUNT_OPTS_H + +struct mount_opts { + const char str[8]; + unsigned long rwmask; + unsigned long rwset; + unsigned long rwnoset; +}; + +struct extra_opts { + char *str; + char *end; + int used_size; + int alloc_size; +}; + +/* + * These options define the function of "mount(2)". + */ +#define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE) + +unsigned long +parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra); + +#endif /* UTILS_MOUNT_OPTS_H */ diff --git a/usr/utils/mv.c b/usr/utils/mv.c new file mode 100644 index 0000000..e3f38ed --- /dev/null +++ b/usr/utils/mv.c @@ -0,0 +1,69 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <linux/limits.h> + +int main(int argc, char *argv[]) +{ + int c, f; + char *p; + struct stat sb; + + f = 0; + do { + c = getopt(argc, argv, "f"); + if (c == EOF) + break; + + switch (c) { + + case 'f': + f = 1; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + return 1; + } + + } while (1); + + if (optind == argc) { + fprintf(stderr, "Usage: %s [-f] source dest\n", argv[0]); + return 1; + } + + memset(&sb, 0, sizeof(struct stat)); + if (stat(argv[argc - 1], &sb) < 0 && argc - optind > 2) { + if (!(S_ISDIR(sb.st_mode))) { + fprintf(stderr, + "multiple targets and %s is not a directory\n", + argv[argc - 1]); + return 1; + } + } + + for (c = optind; c < argc - 1; c++) { + char target[PATH_MAX]; + + p = strrchr(argv[c], '/'); + p++; + + if (S_ISDIR(sb.st_mode)) + snprintf(target, PATH_MAX, "%s/%s", argv[argc - 1], p); + else + snprintf(target, PATH_MAX, "%s", argv[argc - 1]); + + if (f) + unlink(target); + + if (rename(argv[c], target) == -1) + perror(target); + } + + return 0; +} diff --git a/usr/utils/nuke.c b/usr/utils/nuke.c new file mode 100644 index 0000000..93a04af --- /dev/null +++ b/usr/utils/nuke.c @@ -0,0 +1,121 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * Simple program which does the same thing as rm -rf, except it takes + * no options and can therefore not get confused by filenames starting + * with -. Similarly, an empty list of inputs is assumed to mean don't + * do anything. + */ + +#include <assert.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +static const char *program; + +static int nuke(const char *what); + +static int nuke_dirent(int len, const char *dir, const char *name) +{ + int bytes = len + strlen(name) + 2; + char path[bytes]; + int xlen; + + xlen = snprintf(path, bytes, "%s/%s", dir, name); + assert(xlen < bytes); + + return nuke(path); +} + +/* Wipe the contents of a directory, but not the directory itself */ +static int nuke_dir(const char *what) +{ + int len = strlen(what); + DIR *dir; + struct dirent *d; + int err = 0; + + dir = opendir(what); + if (!dir) { + /* EACCES means we can't read it. Might be empty and removable; + if not, the rmdir() in nuke() will trigger an error. */ + return (errno == EACCES) ? 0 : errno; + } + + while ((d = readdir(dir))) { + /* Skip . and .. */ + if (d->d_name[0] == '.' && + (d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; + + err = nuke_dirent(len, what, d->d_name); + if (err) { + closedir(dir); + return err; + } + } + + closedir(dir); + + return 0; +} + +static int nuke(const char *what) +{ + int rv; + int err = 0; + + rv = unlink(what); + if (rv < 0) { + if (errno == EISDIR) { + /* It's a directory. */ + err = nuke_dir(what); + if (!err) + rmdir(what); + } + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int i; + + program = argv[0]; + + for (i = 1; i < argc; i++) + nuke(argv[i]); + + return 0; +} diff --git a/usr/utils/pivot_root.c b/usr/utils/pivot_root.c new file mode 100644 index 0000000..41b2ea0 --- /dev/null +++ b/usr/utils/pivot_root.c @@ -0,0 +1,19 @@ +/* Change the root file system */ + +/* Written 2000 by Werner Almesberger */ + +#include <stdio.h> +#include <sys/mount.h> + +int main(int argc, const char **argv) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s new_root put_old\n", argv[0]); + return 1; + } + if (pivot_root(argv[1], argv[2]) < 0) { + perror("pivot_root"); + return 1; + } + return 0; +} diff --git a/usr/utils/readlink.c b/usr/utils/readlink.c new file mode 100644 index 0000000..1f16c02 --- /dev/null +++ b/usr/utils/readlink.c @@ -0,0 +1,57 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> + +const char *progname; + +static __noreturn usage(void) +{ + fprintf(stderr, "Usage: %s [-f] link...\n", progname); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int c, f_flag = 0; + const char *name; + char link_name[PATH_MAX]; + int rv; + + progname = argv[0]; + + do { + c = getopt(argc, argv, "f"); + if (c == EOF) + break; + switch (c) { + case 'f': + f_flag = 1; + break; + + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + usage(); + } + } while (1); + + if (optind == argc) + usage(); + + argv += optind; + while ((name = *argv++)) { + if (f_flag) + rv = realpath(name, link_name) ? strlen(link_name) : -1; + else + rv = readlink(name, link_name, sizeof link_name - 1); + if (rv < 0) { + perror(name); + exit(1); + } + link_name[rv] = '\n'; + _fwrite(link_name, rv+1, stdout); + } + + return 0; +} diff --git a/usr/utils/sleep.c b/usr/utils/sleep.c new file mode 100644 index 0000000..991cdbe --- /dev/null +++ b/usr/utils/sleep.c @@ -0,0 +1,26 @@ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <errno.h> + +int main(int argc, char *argv[]) +{ + struct timespec ts; + char *p; + + if (argc != 2) + goto err; + + p = strtotimespec(argv[1], &ts); + if (*p) + goto err; + + while (nanosleep(&ts, &ts) == -1 && errno == EINTR) + ; + + return 0; + +err: + fprintf(stderr, "Usage: %s seconds[.fraction]\n", argv[0]); + return 1; +} diff --git a/usr/utils/sync.c b/usr/utils/sync.c new file mode 100644 index 0000000..de3093c --- /dev/null +++ b/usr/utils/sync.c @@ -0,0 +1,7 @@ +#include <unistd.h> + +int main(void) +{ + sync(); + return 0; +} diff --git a/usr/utils/true.c b/usr/utils/true.c new file mode 100644 index 0000000..31dbf45 --- /dev/null +++ b/usr/utils/true.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/usr/utils/umount.c b/usr/utils/umount.c new file mode 100644 index 0000000..41275f7 --- /dev/null +++ b/usr/utils/umount.c @@ -0,0 +1,51 @@ +/* + * by rmk + */ +#include <sys/mount.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +char *progname; + +int main(int argc, char *argv[]) +{ + int c, flag = 0; + + progname = argv[0]; + + do { + c = getopt(argc, argv, "fli"); + if (c == EOF) + break; + switch (c) { + case 'f': + flag |= MNT_FORCE; + break; + case 'l': + flag |= MNT_DETACH; + break; + case 'i': + /* ignore for now; no support for umount helpers */ + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + progname, optopt); + exit(1); + } + } while (1); + + if (optind + 1 != argc) { + fprintf(stderr, "Usage: %s [-f] [-l] [-i] mntpoint\n", + progname); + return 1; + } + + if (umount2(argv[optind], flag) == -1) { + perror("umount2"); + return 255; + } + + return 0; +} diff --git a/usr/utils/uname.c b/usr/utils/uname.c new file mode 100644 index 0000000..6ea4dbe --- /dev/null +++ b/usr/utils/uname.c @@ -0,0 +1,155 @@ +/* + * by tlh + * + * The uname program for system information: kernel name, kernel + * release, kernel release, machine, processor, platform, os and + * hostname. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/utsname.h> + +enum uname_fields { + UN_SYSNAME, + UN_NODENAME, + UN_RELEASE, + UN_VERSION, + UN_MACHINE, +#if NOT_IMPLEMENTED_PROCESSOR + UN_PROCESSOR, +#endif + UN_HARDWARE, +#if NOT_IMPLEMENTED_OS + UN_OS, +#endif + UN_NR_FIELDS +}; + +static void usage(FILE *stream, const char *progname) +{ + fprintf(stream, + "Usage: %s [OPTION] . . .\n" + "Print system information, No options defaults to -s.\n" + "\n" + " -a print all the information in the same order as follows below\n" + " -s kernel name\n" + " -n network node name (hostname)\n" + " -r kernel release\n" + " -v kernel version\n" " -m machine hardware name\n" +#if NOT_IMPLEMENTED_PROCESSOR + " -p processor type\n" +#endif + " -i hardware platform\n" +#if NOT_IMPLEMENTED_OS + " -o operating system\n" +#endif + "\n" " -h help/usage\n" "\n", progname); +} + +static char *make_hardware(const char *machine) +{ + char *hardware; + + hardware = strdup(machine); + if (!hardware) { + fprintf(stderr, "strdup() failed: %s\n", strerror(errno)); + goto end; + } + if (strlen(hardware) == 4 + && hardware[0] == 'i' && hardware[2] == '8' && hardware[3] == '6') { + hardware[1] = '3'; + } +end: + return hardware; +} + +int main(int argc, char *argv[]) +{ + int ec = 1; + int opt; + int i; + int nr_pr; + struct utsname buf; + char *uname_fields[UN_NR_FIELDS] = { NULL }; + + if (-1 == uname(&buf)) { + fprintf(stderr, "uname() failure: %s\n", strerror(errno)); + goto end; + } + + if (1 == argc) + /* no options given - default to -s */ + uname_fields[UN_SYSNAME] = buf.sysname; + + while ((opt = getopt(argc, argv, "asnrvmpioh")) != -1) { + switch (opt) { + case 'a': + uname_fields[UN_SYSNAME] = buf.sysname; + uname_fields[UN_NODENAME] = buf.nodename; + uname_fields[UN_RELEASE] = buf.release; + uname_fields[UN_VERSION] = buf.version; + uname_fields[UN_MACHINE] = buf.machine; + uname_fields[UN_HARDWARE] = make_hardware(buf.machine); + if (!uname_fields[UN_HARDWARE]) + goto end; + break; + case 's': + uname_fields[UN_SYSNAME] = buf.sysname; + break; + case 'n': + uname_fields[UN_NODENAME] = buf.nodename; + break; + case 'r': + uname_fields[UN_RELEASE] = buf.release; + break; + case 'v': + uname_fields[UN_VERSION] = buf.version; + break; + case 'm': + uname_fields[UN_MACHINE] = buf.machine; + break; +#if NOT_IMPLEMENTED_PROCESSOR + case 'p': + break; +#endif + case 'i': + uname_fields[UN_HARDWARE] = make_hardware(buf.machine); + if (!uname_fields[UN_HARDWARE]) + goto end; + break; +#if NOT_IMPLEMENTED_OS + case 'o': + break; +#endif + case 'h': + usage(stdout, argv[0]); + ec = 0; + goto end; + break; + default: + usage(stderr, argv[0]); + goto end; + break; + } + } + + for (nr_pr = 0, i = UN_SYSNAME; i < UN_NR_FIELDS; i++) { + if (!uname_fields[i]) + continue; + if (nr_pr) + fputc(' ', stdout); + fputs(uname_fields[i], stdout); + nr_pr++; + } + fputc('\n', stdout); + + ec = 0; + +end: + if (uname_fields[UN_HARDWARE]) + free(uname_fields[UN_HARDWARE]); + return ec; +} |